From b4ef445281d613443acac90263a9a77123fbfd61 Mon Sep 17 00:00:00 2001 From: Uncle Stretch Date: Mon, 28 Jul 2025 23:34:42 +0300 Subject: [PATCH] make ability to have multiple default endpoints for the network Signed-off-by: Uncle Stretch --- pallets/networks/Cargo.toml | 2 +- pallets/networks/src/lib.rs | 63 +++++++-- pallets/networks/src/tests.rs | 236 ++++++++++++++++++++++++++++++++-- 3 files changed, 274 insertions(+), 27 deletions(-) diff --git a/pallets/networks/Cargo.toml b/pallets/networks/Cargo.toml index 857a05e..1aba5a4 100644 --- a/pallets/networks/Cargo.toml +++ b/pallets/networks/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-networks" -version = "0.1.11" +version = "0.1.12" license.workspace = true authors.workspace = true edition.workspace = true diff --git a/pallets/networks/src/lib.rs b/pallets/networks/src/lib.rs index 109edf9..38ea5a8 100644 --- a/pallets/networks/src/lib.rs +++ b/pallets/networks/src/lib.rs @@ -52,7 +52,7 @@ impl Default for NetworkType { #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct NetworkData { pub chain_name: Vec, - pub default_endpoint: Vec, + pub default_endpoints: Vec>, pub gatekeeper: Vec, pub topic_name: Vec, pub network_type: NetworkType, @@ -168,7 +168,16 @@ pub mod module { }, NetworkEndpointUpdated { chain_id: T::NetworkId, - default_endpoint: Vec, + index: u32, + endpoint: Vec, + }, + NetworkEndpointRemoved { + chain_id: T::NetworkId, + index: u32, + }, + NetworkEndpointAdded { + chain_id: T::NetworkId, + endpoint: Vec, }, NetworkFinalityDelayUpdated { chain_id: T::NetworkId, @@ -280,7 +289,10 @@ pub mod module { #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::register_network( network.chain_name.len() as u32, - network.default_endpoint.len() as u32, + network.default_endpoints + .iter() + .map(|endpoint| endpoint.len()) + .sum::() as u32, ))] pub fn register_network( origin: OriginFor, @@ -306,15 +318,19 @@ pub mod module { #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::update_network_endpoint( - default_endpoint.len() as u32 + maybe_endpoint + .as_ref() + .map(|endpoint| endpoint.len()) + .unwrap_or_default() as u32 ))] pub fn update_network_endpoint( origin: OriginFor, chain_id: T::NetworkId, - default_endpoint: Vec, + maybe_index: Option, + maybe_endpoint: Option>, ) -> DispatchResult { T::UpdateOrigin::ensure_origin_or_root(origin)?; - Self::do_update_network_endpoint(chain_id, default_endpoint) + Self::do_update_network_endpoint(chain_id, maybe_index, maybe_endpoint) } #[pallet::call_index(3)] @@ -462,19 +478,38 @@ impl Pallet { /// Update existent network default endpoint. pub fn do_update_network_endpoint( chain_id: T::NetworkId, - default_endpoint: Vec, + maybe_index: Option, + maybe_endpoint: Option>, ) -> DispatchResult { Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::::NetworkDoesNotExist); - let net = maybe_network.as_mut().unwrap(); - net.default_endpoint = default_endpoint.clone(); - *maybe_network = Some(net.clone()); + let updated_network = maybe_network.as_mut().unwrap(); + match (maybe_index, maybe_endpoint) { + (Some(index), Some(endpoint)) => { + if let Some(previous_endpoint) = + updated_network.default_endpoints.get_mut(index as usize) + { + *previous_endpoint = endpoint.clone(); + Self::deposit_event(Event::::NetworkEndpointUpdated { + chain_id, + index, + endpoint, + }); + } + } + (None, Some(endpoint)) => { + updated_network.default_endpoints.push(endpoint.clone()); + Self::deposit_event(Event::::NetworkEndpointAdded { chain_id, endpoint }); + } + (Some(index), None) => { + updated_network.default_endpoints.remove(index as usize); + Self::deposit_event(Event::::NetworkEndpointRemoved { chain_id, index }); + } + (None, None) => {} + } + *maybe_network = Some(updated_network.clone()); Ok(()) })?; - Self::deposit_event(Event::::NetworkEndpointUpdated { - chain_id, - default_endpoint, - }); Ok(()) } diff --git a/pallets/networks/src/tests.rs b/pallets/networks/src/tests.rs index 2cfd92d..7188e14 100644 --- a/pallets/networks/src/tests.rs +++ b/pallets/networks/src/tests.rs @@ -12,7 +12,10 @@ fn prepare_network_data() -> (u32, NetworkData) { 1u32, NetworkData { chain_name: "Ethereum".into(), - default_endpoint: "https:://some-endpoint.my-server.com/v1/my-super-secret-key".into(), + default_endpoints: vec![ + "https:://some-endpoint.my-server.com/v1/my-super-secret-key".into(), + "https:://another-endpoint.my-server.com/v1/my-super-secret-key".into(), + ], finality_delay: 69, rate_limit_delay: 69, block_distance: 69, @@ -113,26 +116,117 @@ fn could_update_network_name_from_authority_account() { } #[test] -fn could_update_network_endpoint_from_authority_account() { +fn could_add_network_endpoint_from_authority_account() { ExtBuilder::build().execute_with(|| { - let new_endpoint = b"https:://google.com".to_vec(); + let raw_endpoint: Vec = + "https:://new-endpoint.my-server.com/v1/my-super-secret-key".into(); let (chain_id, network) = prepare_network_data(); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_endpoint( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, - new_endpoint.clone() + None, + Some(raw_endpoint.clone()), + )); + System::assert_last_event(RuntimeEvent::GhostNetworks( + crate::Event::NetworkEndpointAdded { + chain_id, + endpoint: raw_endpoint.clone(), + }, + )); + let current_network = Networks::::get(chain_id).unwrap(); + assert_eq!( + current_network.default_endpoints.len(), + network.default_endpoints.len() + 1 + ); + assert_eq!( + current_network + .default_endpoints + .last() + .cloned() + .unwrap_or_default(), + raw_endpoint + ); + assert_ne!(&network, ¤t_network); + }); +} + +#[test] +fn could_remove_network_endpoint_from_authority_account() { + ExtBuilder::build().execute_with(|| { + let index_to_remove = 0u32; + let (chain_id, network) = prepare_network_data(); + register_and_check_network(chain_id, network.clone()); + assert_ok!(GhostNetworks::update_network_endpoint( + RuntimeOrigin::signed(UpdaterAccount::get()), + chain_id, + Some(index_to_remove), + None, + )); + System::assert_last_event(RuntimeEvent::GhostNetworks( + crate::Event::NetworkEndpointRemoved { + chain_id, + index: index_to_remove, + }, + )); + let current_network = Networks::::get(chain_id).unwrap(); + assert_eq!( + current_network.default_endpoints.len(), + network.default_endpoints.len() - 1 + ); + assert_ne!( + current_network + .default_endpoints + .get(index_to_remove as usize), + network.default_endpoints.get(index_to_remove as usize) + ); + assert_ne!(&network, ¤t_network); + }); +} + +#[test] +fn could_update_network_endpoint_from_authority_account() { + ExtBuilder::build().execute_with(|| { + let index_to_update = 0u32; + let raw_endpoint: Vec = + "https:://new-endpoint.my-server.com/v1/my-super-secret-key".into(); + let (chain_id, network) = prepare_network_data(); + register_and_check_network(chain_id, network.clone()); + assert_ok!(GhostNetworks::update_network_endpoint( + RuntimeOrigin::signed(UpdaterAccount::get()), + chain_id, + Some(index_to_update), + Some(raw_endpoint.clone()), )); System::assert_last_event(RuntimeEvent::GhostNetworks( crate::Event::NetworkEndpointUpdated { chain_id, - default_endpoint: new_endpoint.clone(), + index: index_to_update, + endpoint: raw_endpoint.clone(), }, )); + let previous_endpoints_len = network.default_endpoints.len(); let mut final_network = network.clone(); - final_network.default_endpoint = new_endpoint; - assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); - assert_ne!(network, final_network); + if let Some(endpoint_by_index) = final_network + .default_endpoints + .get_mut(index_to_update as usize) + { + *endpoint_by_index = raw_endpoint.clone(); + } + let current_network = Networks::::get(chain_id).unwrap(); + assert_eq!( + current_network.default_endpoints.len(), + previous_endpoints_len + ); + assert_eq!( + current_network + .default_endpoints + .get(index_to_update as usize) + .cloned() + .unwrap_or_default(), + raw_endpoint + ); + assert_ne!(&network, &final_network); }); } @@ -365,13 +459,17 @@ fn could_not_update_network_name_from_random_account() { #[test] fn could_not_update_network_endpoint_from_random_account() { ExtBuilder::build().execute_with(|| { + let index_to_update = 0u32; + let raw_endpoint: Vec = + "https:://new-endpoint.my-server.com/v1/my-super-secret-key".into(); let (chain_id, network) = prepare_network_data(); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_endpoint( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, - "https:://google.com".into() + Some(index_to_update), + Some(raw_endpoint.clone()), ), DispatchError::BadOrigin ); @@ -379,7 +477,8 @@ fn could_not_update_network_endpoint_from_random_account() { GhostNetworks::update_network_endpoint( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, - "https:://google.com".into() + Some(index_to_update), + Some(raw_endpoint.clone()), ), DispatchError::BadOrigin ); @@ -387,7 +486,83 @@ fn could_not_update_network_endpoint_from_random_account() { GhostNetworks::update_network_endpoint( RuntimeOrigin::signed(RandomAccount::get()), chain_id, - "https:://google.com".into() + Some(index_to_update), + Some(raw_endpoint.clone()), + ), + DispatchError::BadOrigin + ); + assert_eq!(Networks::::get(chain_id), Some(network)); + }); +} + +#[test] +fn could_not_add_network_endpoint_from_random_account() { + ExtBuilder::build().execute_with(|| { + let raw_endpoint: Vec = + "https:://new-endpoint.my-server.com/v1/my-super-secret-key".into(); + let (chain_id, network) = prepare_network_data(); + register_and_check_network(chain_id, network.clone()); + assert_err!( + GhostNetworks::update_network_endpoint( + RuntimeOrigin::signed(RegistererAccount::get()), + chain_id, + None, + Some(raw_endpoint.clone()), + ), + DispatchError::BadOrigin + ); + assert_err!( + GhostNetworks::update_network_endpoint( + RuntimeOrigin::signed(RemoverAccount::get()), + chain_id, + None, + Some(raw_endpoint.clone()), + ), + DispatchError::BadOrigin + ); + assert_err!( + GhostNetworks::update_network_endpoint( + RuntimeOrigin::signed(RandomAccount::get()), + chain_id, + None, + Some(raw_endpoint.clone()), + ), + DispatchError::BadOrigin + ); + assert_eq!(Networks::::get(chain_id), Some(network)); + }); +} + +#[test] +fn could_not_remove_network_endpoint_from_random_account() { + ExtBuilder::build().execute_with(|| { + let index_to_remove = 0u32; + let (chain_id, network) = prepare_network_data(); + register_and_check_network(chain_id, network.clone()); + assert_err!( + GhostNetworks::update_network_endpoint( + RuntimeOrigin::signed(RegistererAccount::get()), + chain_id, + Some(index_to_remove), + None, + ), + DispatchError::BadOrigin + ); + assert_err!( + GhostNetworks::update_network_endpoint( + RuntimeOrigin::signed(RemoverAccount::get()), + chain_id, + Some(index_to_remove), + None, + ), + DispatchError::BadOrigin + ); + assert_err!( + GhostNetworks::update_network_endpoint( + RuntimeOrigin::signed(RandomAccount::get()), + chain_id, + Some(index_to_remove), + None, ), DispatchError::BadOrigin ); @@ -688,7 +863,44 @@ fn could_not_update_endpoint_for_non_existent_network() { GhostNetworks::update_network_endpoint( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, - "https:://google.com".into() + Some(0u32), + Some("https:://new-endpoint.my-server.com/v1/my-super-secret-key".into()), + ), + crate::Error::::NetworkDoesNotExist + ); + assert_eq!(Networks::::get(chain_id), None); + }); +} + +#[test] +fn could_not_add_endpoint_for_non_existent_network() { + ExtBuilder::build().execute_with(|| { + let chain_id: u32 = 1; + assert_eq!(Networks::::get(chain_id), None); + assert_err!( + GhostNetworks::update_network_endpoint( + RuntimeOrigin::signed(UpdaterAccount::get()), + chain_id, + None, + Some("https:://new-endpoint.my-server.com/v1/my-super-secret-key".into()), + ), + crate::Error::::NetworkDoesNotExist + ); + assert_eq!(Networks::::get(chain_id), None); + }); +} + +#[test] +fn could_not_remove_endpoint_for_non_existent_network() { + ExtBuilder::build().execute_with(|| { + let chain_id: u32 = 1; + assert_eq!(Networks::::get(chain_id), None); + assert_err!( + GhostNetworks::update_network_endpoint( + RuntimeOrigin::signed(UpdaterAccount::get()), + chain_id, + Some(0u32), + None, ), crate::Error::::NetworkDoesNotExist );