use super::*; use frame_support::{assert_err, assert_ok}; use mock::{ ExtBuilder, GhostNetworks, MaxNetworks, RandomAccount, RegistererAccount, RemoverAccount, RewardCurve, RuntimeEvent, RuntimeOrigin, System, Test, UpdaterAccount, }; use pallet_staking::EraPayout; use sp_runtime::DispatchError; const CURRENT_CURVE: NetworkCurve = NetworkCurve::Secp256k1; fn prepare_network_data( maybe_in_share: Option, maybe_out_share: Option, ) -> (u32, NetworkData) { ( 1u32, ghost_helpers::networks::NetworkDataBuilder::default() .with_gatekeeper( hex::decode("02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9") .expect("Invalid public key"), ) .with_default_endpoints(vec![ b"https://some-mainnet.infura.io/v3/demo-key".to_vec(), b"https://rpc4ever.info/api".to_vec(), ]) .with_selector(vec![1u8; 4]) .with_network_type(NetworkType::Evm) .with_network_curve(CURRENT_CURVE) .with_rate_limit_delay(6) .with_finality_delay(69) .with_block_deviation(420) .with_incoming_share(maybe_in_share.unwrap_or_default()) .with_outgoing_share(maybe_out_share.unwrap_or_default()) .build(), ) } fn register_and_check_network(chain_id: u32, network: NetworkData) { let network_curve = network.curve; assert_ok!(GhostNetworks::register_network( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, network.clone() )); assert_eq!(Networks::::get(chain_id), Some(network)); assert_eq!(NetworkIndexes::::get(), vec![chain_id]); assert!(NetworkCurves::::contains_key(&network_curve)); assert!(NetworkCurvesVec::::get().contains(&network_curve)); } #[test] fn could_add_network_from_authority() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(None, None); assert_eq!(Networks::::get(chain_id), None); assert_eq!(NetworkIndexes::::get(), vec![]); assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); assert!(!NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); assert_ok!(GhostNetworks::register_network( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, network.clone(), )); System::assert_last_event(RuntimeEvent::GhostNetworks( crate::Event::NetworkRegistered { chain_id }, )); assert_eq!(Networks::::get(chain_id), Some(network)); assert_eq!(NetworkIndexes::::get(), vec![chain_id]); assert!(NetworkCurves::::contains_key(&CURRENT_CURVE)); assert!(NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); }); } #[test] fn could_not_add_network_from_random_account() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(None, None); assert_eq!(Networks::::get(chain_id), None); assert_eq!(NetworkIndexes::::get(), vec![]); assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); assert!(!NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); assert_err!( GhostNetworks::register_network( RuntimeOrigin::signed(RandomAccount::get()), chain_id, network.clone(), ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::register_network( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, network.clone(), ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::register_network( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, network, ), DispatchError::BadOrigin ); assert_eq!(Networks::::get(chain_id), None); assert_eq!(NetworkIndexes::::get(), vec![]); assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); assert!(!NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); }); } #[test] fn could_update_network_selector_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_selector = BoundedVec::>::try_from(vec![69, 69, 69, 69]) .unwrap(); let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_selector( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, new_selector.clone() )); System::assert_last_event(RuntimeEvent::GhostNetworks( crate::Event::NetworkSelectorUpdated { chain_id }, )); let mut final_network = network.clone(); final_network.selector = new_selector; assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); assert_ne!(network, final_network); }); } #[test] fn could_add_network_endpoint_from_authority_account() { ExtBuilder::build().execute_with(|| { let raw_endpoint = BoundedVec::>::try_from( b"https:://new-endpoint.my-server.com/v1/my-super-secret-key".to_vec(), ).unwrap(); let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_endpoint( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, None, Some(raw_endpoint.clone()), )); System::assert_last_event(RuntimeEvent::GhostNetworks( crate::Event::NetworkEndpointAdded { chain_id }, )); 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(None, None); 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 = BoundedVec::>::try_from( b"https:://new-endpoint.my-server.com/v1/my-super-secret-key".to_vec(), ).unwrap(); let (chain_id, network) = prepare_network_data(None, None); 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, index: index_to_update }, )); let previous_endpoints_len = network.default_endpoints.len(); let mut final_network = network.clone(); 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); }); } #[test] fn could_update_network_finality_delay_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_finality_delay = 1337; let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_finality_delay( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, new_finality_delay )); System::assert_last_event(RuntimeEvent::GhostNetworks( crate::Event::NetworkFinalityDelayUpdated { chain_id, finality_delay: new_finality_delay, }, )); let mut final_network = network.clone(); final_network.finality_delay = new_finality_delay; assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); assert_ne!(network, final_network); }); } #[test] fn could_update_network_rate_limit_delay_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_rate_limit_delay = 1337; let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_rate_limit_delay( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, new_rate_limit_delay )); System::assert_last_event(RuntimeEvent::GhostNetworks( crate::Event::NetworkRateLimitDelayUpdated { chain_id, rate_limit_delay: new_rate_limit_delay, }, )); let mut final_network = network.clone(); final_network.rate_limit_delay = new_rate_limit_delay; assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); assert_ne!(network, final_network); }); } #[test] fn could_update_network_block_deviation_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_block_deviation = 1337; let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_block_deviation( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, new_block_deviation )); System::assert_last_event(RuntimeEvent::GhostNetworks( crate::Event::NetworkBlockDeviationUpdated { chain_id, block_deviation: new_block_deviation, }, )); let mut final_network = network.clone(); final_network.block_deviation = new_block_deviation; assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); assert_ne!(network, final_network); }); } #[test] fn could_update_network_type_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_type = NetworkType::Utxo; let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_type( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, new_type.clone() )); System::assert_last_event(RuntimeEvent::GhostNetworks( crate::Event::NetworkTypeUpdated { chain_id, network_type: new_type.clone(), }, )); let mut final_network = network.clone(); final_network.r#type = new_type; assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); assert_ne!(network, final_network); }); } #[test] fn could_update_network_curve_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_curve = NetworkCurve::Ed25519; let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert!(!NetworkCurves::::contains_key(&new_curve)); assert!(NetworkCurves::::contains_key(&CURRENT_CURVE)); assert!(!NetworkCurvesVec::::get().contains(&new_curve)); assert!(NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); assert_ok!(GhostNetworks::update_network_curve( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, new_curve.clone() )); System::assert_last_event(RuntimeEvent::GhostNetworks( crate::Event::NetworkCurveUpdated { chain_id, network_curve: new_curve.clone(), }, )); let mut final_network = network.clone(); final_network.r#curve = new_curve; assert!(NetworkCurves::::contains_key(&new_curve)); assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); assert!(NetworkCurvesVec::::get().contains(&new_curve)); assert!(!NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); assert_ne!(network, final_network); }); } #[test] fn could_update_network_gatekeeper_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_gatekeeper = BoundedVec::>::try_from( hex::decode("04b2c7e3d9f9f8a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0") .expect("Invalid public key"), ).unwrap(); let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_network_gatekeeper( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, new_gatekeeper.clone() )); System::assert_last_event(RuntimeEvent::GhostNetworks( crate::Event::NetworkGatekeeperUpdated { chain_id }, )); let mut final_network = network.clone(); final_network.gatekeeper = new_gatekeeper; assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); assert_ne!(network, final_network); }); } #[test] fn could_update_incoming_network_share_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_incoming_share = 69; let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_incoming_network_share( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, new_incoming_share )); System::assert_last_event(RuntimeEvent::GhostNetworks( crate::Event::NetworkIncomingShareUpdated { chain_id, incoming_share: new_incoming_share, }, )); let mut final_network = network.clone(); final_network.incoming_share = new_incoming_share; assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); assert_ne!(network, final_network); }); } #[test] fn could_update_outgoing_network_share_from_authority_account() { ExtBuilder::build().execute_with(|| { let new_outgoing_share = 69; let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::update_outgoing_network_share( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, new_outgoing_share )); System::assert_last_event(RuntimeEvent::GhostNetworks( crate::Event::NetworkOutgoingShareUpdated { chain_id, outgoing_share: new_outgoing_share, }, )); let mut final_network = network.clone(); final_network.outgoing_share = new_outgoing_share; assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); assert_ne!(network, final_network); }); } #[test] fn could_not_update_network_endpoint_from_random_account() { ExtBuilder::build().execute_with(|| { let index_to_update = 0u32; let raw_endpoint = BoundedVec::>::try_from( b"https:://new-endpoint.my-server.com/v1/my-super-secret-key".to_vec(), ).unwrap(); let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_endpoint( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, Some(index_to_update), Some(raw_endpoint.clone()), ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_endpoint( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, Some(index_to_update), Some(raw_endpoint.clone()), ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_endpoint( RuntimeOrigin::signed(RandomAccount::get()), chain_id, 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 = BoundedVec::>::try_from( b"https:://new-endpoint.my-server.com/v1/my-super-secret-key".to_vec(), ).unwrap(); let (chain_id, network) = prepare_network_data(None, None); 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(None, None); 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 ); assert_eq!(Networks::::get(chain_id), Some(network)); }); } #[test] fn could_not_update_network_finality_delay_from_random_account() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(None, None); let finality_delay = 1337; register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_finality_delay( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, finality_delay ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_finality_delay( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, finality_delay ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_finality_delay( RuntimeOrigin::signed(RandomAccount::get()), chain_id, finality_delay ), DispatchError::BadOrigin ); assert_eq!(Networks::::get(chain_id), Some(network)); }); } #[test] fn could_not_update_network_rate_limit_delay_from_random_account() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(None, None); let rate_limit_delay = 1337; register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_rate_limit_delay( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, rate_limit_delay ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_rate_limit_delay( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, rate_limit_delay ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_rate_limit_delay( RuntimeOrigin::signed(RandomAccount::get()), chain_id, rate_limit_delay ), DispatchError::BadOrigin ); assert_eq!(Networks::::get(chain_id), Some(network)); }); } #[test] fn could_not_update_network_release_delay_from_random_account() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(None, None); let block_deviation = 1337; register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_block_deviation( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, block_deviation ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_block_deviation( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, block_deviation ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_block_deviation( RuntimeOrigin::signed(RandomAccount::get()), chain_id, block_deviation ), DispatchError::BadOrigin ); assert_eq!(Networks::::get(chain_id), Some(network)); }); } #[test] fn could_not_update_network_type_from_random_account() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_type( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, NetworkType::Utxo ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_type( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, NetworkType::Utxo ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_type( RuntimeOrigin::signed(RandomAccount::get()), chain_id, NetworkType::Utxo ), DispatchError::BadOrigin ); assert_eq!(Networks::::get(chain_id), Some(network)); }); } #[test] fn could_not_update_network_curve_from_random_account() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_curve( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, NetworkCurve::Ed25519, ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_curve( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, NetworkCurve::Ed25519, ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_curve( RuntimeOrigin::signed(RandomAccount::get()), chain_id, NetworkCurve::Ed25519, ), DispatchError::BadOrigin ); assert_eq!(Networks::::get(chain_id), Some(network)); }); } #[test] fn could_not_update_network_gatekeeper_from_random_account() { ExtBuilder::build().execute_with(|| { let new_gatekeeper = BoundedVec::>::try_from( hex::decode("04b2c7e3d9f9f8a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0") .expect("Invalid public key"), ).unwrap(); let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_network_gatekeeper( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, new_gatekeeper.clone(), ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_gatekeeper( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, new_gatekeeper.clone(), ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_network_gatekeeper( RuntimeOrigin::signed(RandomAccount::get()), chain_id, new_gatekeeper, ), DispatchError::BadOrigin ); assert_eq!(Networks::::get(chain_id), Some(network)); }); } #[test] fn could_not_update_network_incoming_share_from_random_account() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_incoming_network_share( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, 69 ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_incoming_network_share( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, 69 ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_incoming_network_share( RuntimeOrigin::signed(RandomAccount::get()), chain_id, 69 ), DispatchError::BadOrigin ); assert_eq!(Networks::::get(chain_id), Some(network)); }); } #[test] fn could_not_update_network_outgoing_share_from_random_account() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::update_outgoing_network_share( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, 69 ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_outgoing_network_share( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, 69 ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::update_outgoing_network_share( RuntimeOrigin::signed(RandomAccount::get()), chain_id, 69 ), DispatchError::BadOrigin ); assert_eq!(Networks::::get(chain_id), Some(network)); }); } #[test] fn could_not_update_endpoint_for_non_existent_network() { ExtBuilder::build().execute_with(|| { let raw_endpoint = BoundedVec::>::try_from( b"https:://new-endpoint.my-server.com/v1/my-super-secret-key".to_vec(), ).unwrap(); 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), Some(raw_endpoint), ), 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 raw_endpoint = BoundedVec::>::try_from( b"https:://new-endpoint.my-server.com/v1/my-super-secret-key".to_vec(), ).unwrap(); 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(raw_endpoint), ), 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 ); assert_eq!(Networks::::get(chain_id), None); }); } #[test] fn could_not_update_finality_delay_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_finality_delay( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, 1337 ), crate::Error::::NetworkDoesNotExist ); assert_eq!(Networks::::get(chain_id), None); }); } #[test] fn could_not_update_rate_limit_delay_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_rate_limit_delay( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, 1337 ), crate::Error::::NetworkDoesNotExist ); assert_eq!(Networks::::get(chain_id), None); }); } #[test] fn could_not_update_release_delay_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_block_deviation( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, 1337 ), crate::Error::::NetworkDoesNotExist ); assert_eq!(Networks::::get(chain_id), None); }); } #[test] fn could_not_update_type_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_type( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, NetworkType::Utxo ), crate::Error::::NetworkDoesNotExist ); assert_eq!(Networks::::get(chain_id), None); }); } #[test] fn could_not_update_curve_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_curve( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, NetworkCurve::Ed25519, ), crate::Error::::NetworkDoesNotExist ); assert_eq!(Networks::::get(chain_id), None); }); } #[test] fn could_not_update_gatekeeper_for_non_existent_network() { ExtBuilder::build().execute_with(|| { let new_gatekeeper = BoundedVec::>::try_from( hex::decode("04b2c7e3d9f9f8a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0") .expect("Invalid public key"), ).unwrap(); let chain_id: u32 = 1; assert_eq!(Networks::::get(chain_id), None); assert_err!( GhostNetworks::update_network_gatekeeper( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, new_gatekeeper, ), crate::Error::::NetworkDoesNotExist ); assert_eq!(Networks::::get(chain_id), None); }); } #[test] fn could_not_update_incoming_share_for_non_existent_network() { ExtBuilder::build().execute_with(|| { let chain_id: u32 = 1; assert_eq!(Networks::::get(chain_id), None); assert_err!( GhostNetworks::update_incoming_network_share( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, 1337 ), crate::Error::::NetworkDoesNotExist ); assert_eq!(Networks::::get(chain_id), None); }); } #[test] fn could_not_update_outgoing_share_for_non_existent_network() { ExtBuilder::build().execute_with(|| { let chain_id: u32 = 1; assert_eq!(Networks::::get(chain_id), None); assert_err!( GhostNetworks::update_outgoing_network_share( RuntimeOrigin::signed(UpdaterAccount::get()), chain_id, 1337 ), crate::Error::::NetworkDoesNotExist ); assert_eq!(Networks::::get(chain_id), None); }); } #[test] fn could_remove_network_from_authority_account() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_ok!(GhostNetworks::remove_network( RuntimeOrigin::signed(RemoverAccount::get()), chain_id, )); assert_eq!(Networks::::get(chain_id), None); assert_eq!(NetworkIndexes::::get(), vec![]); assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); assert!(!NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); }); } #[test] fn could_not_remove_network_from_random_account() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); assert_err!( GhostNetworks::remove_network( RuntimeOrigin::signed(RegistererAccount::get()), chain_id ), DispatchError::BadOrigin ); assert_err!( GhostNetworks::remove_network(RuntimeOrigin::signed(UpdaterAccount::get()), chain_id), DispatchError::BadOrigin ); assert_err!( GhostNetworks::remove_network(RuntimeOrigin::signed(RandomAccount::get()), chain_id), DispatchError::BadOrigin ); assert_eq!(Networks::::get(chain_id), Some(network)); assert_eq!(NetworkIndexes::::get(), vec![chain_id]); assert!(NetworkCurves::::contains_key(&CURRENT_CURVE)); assert!(NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); }); } #[test] fn could_not_remove_non_existent_network() { ExtBuilder::build().execute_with(|| { let chain_id: u32 = 1; assert_eq!(Networks::::get(chain_id), None); assert_err!( GhostNetworks::remove_network(RuntimeOrigin::signed(RemoverAccount::get()), chain_id), crate::Error::::NetworkDoesNotExist ); assert_eq!(Networks::::get(chain_id), None); assert_eq!(NetworkIndexes::::get(), vec![]); assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); assert!(!NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); }); } #[test] fn bridge_storage_is_empty_by_default() { ExtBuilder::build().execute_with(|| { let state = NetworkImbalance::::get(); assert_eq!(state.incoming, Default::default()); assert_eq!(state.outgoing, Default::default()); assert_eq!(state.curve_share, Default::default()); }); } #[test] fn check_substrate_guarantees_not_to_overflow_u128() { ExtBuilder::build().execute_with(|| { let reward_curve = RewardCurve::get(); let mut n: u128 = 69; let mut d: u128 = 100; loop { n = match n.checked_mul(1_000) { Some(value) => value, None => break, }; d = match d.checked_mul(1_000) { Some(value) => value, None => break, }; assert_eq!( reward_curve.calculate_for_fraction_times_denominator(n, d), d ); } }); } #[test] fn check_substrate_guarantees_not_to_overflow_u64() { ExtBuilder::build().execute_with(|| { let reward_curve = RewardCurve::get(); let mut n: u64 = 69; let mut d: u64 = 100; loop { n = match n.checked_mul(1_000) { Some(value) => value, None => break, }; d = match d.checked_mul(1_000) { Some(value) => value, None => break, }; assert_eq!( reward_curve.calculate_for_fraction_times_denominator(n, d), d ); } }); } #[test] fn check_substrate_guarantees_not_to_overflow_u32() { ExtBuilder::build().execute_with(|| { let reward_curve = RewardCurve::get(); let mut n: u32 = 69; let mut d: u32 = 100; loop { n = match n.checked_mul(1_000) { Some(value) => value, None => break, }; d = match d.checked_mul(1_000) { Some(value) => value, None => break, }; assert_eq!( reward_curve.calculate_for_fraction_times_denominator(n, d), d ); } }); } #[test] fn error_on_max_networks_overflow() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(None, None); let max_networks = MaxNetworks::get(); for index in 0..max_networks { let other_chain_id = chain_id.saturating_add(index); assert_ok!(GhostNetworks::register_network( RuntimeOrigin::signed(RegistererAccount::get()), other_chain_id, network.clone(), )); } assert_err!( GhostNetworks::register_network( RuntimeOrigin::signed(RegistererAccount::get()), chain_id.saturating_add(max_networks), network.clone(), ), crate::Error::::TooManyNetworks, ); }); } #[test] fn bridge_inbound_and_outbound_flows() { ExtBuilder::build().execute_with(|| { let in_share = Some(50_000_000); // 5% let out_share = Some(100_000_000); // 10% let (chain_id, network) = prepare_network_data(in_share, out_share); register_and_check_network(chain_id, network); let inbound_amount: u128 = 10_000; let expected_in_curve: u128 = 500; let expected_in_final: u128 = 9500; let final_amount = Pallet::::register_incoming( &chain_id, inbound_amount, ); assert_ok!(final_amount, expected_in_final); let imbalance = NetworkImbalance::::get(); assert_eq!(imbalance.incoming, expected_in_final); assert_eq!(imbalance.curve_share, expected_in_curve); assert_eq!(imbalance.outgoing, 0); let gatekeeper_balance = GatekeeperAmounts::::get(&chain_id); assert_eq!(gatekeeper_balance, inbound_amount); let outbound_amount: u128 = 5_000; let expected_out_curve: u128 = 500; let expected_out_final: u128 = 4500; let final_amount = Pallet::::register_outgoing(&chain_id, outbound_amount); assert_ok!(final_amount, expected_out_final); let current_imbalance = NetworkImbalance::::get(); assert_eq!(current_imbalance.incoming, expected_in_final); assert_eq!(current_imbalance.outgoing, expected_out_final); assert_eq!(current_imbalance.curve_share, 1000); assert_eq!(current_imbalance.curve_share, expected_in_curve + expected_out_curve); let final_gatekeeper_balance = GatekeeperAmounts::::get(&chain_id); assert_eq!(final_gatekeeper_balance, 5_000); let evil_outbound_amount: u128 = 6_000; let final_amount_result = Pallet::::register_outgoing(&chain_id, evil_outbound_amount); assert!(final_amount_result.is_err()); let post_fail_imbalance = NetworkImbalance::::get(); assert_eq!(post_fail_imbalance.curve_share, 1000); assert_eq!(post_fail_imbalance.outgoing, expected_out_final); let total_accumulated_shares = 1000; assert_eq!(current_imbalance.curve_share, total_accumulated_shares); let total_staked_ideal: u128 = 69_000_000; let total_issuance: u128 = 100_000_000; let (payout, rest_payout) = BridgedInflationCurve::::era_payout( total_staked_ideal, total_issuance, 0, ); assert_eq!(payout + rest_payout, total_accumulated_shares); let post_era_imbalance = NetworkImbalance::::get(); assert_eq!(post_era_imbalance.incoming, 0); assert_eq!(post_era_imbalance.outgoing, 0); assert_eq!(post_era_imbalance.curve_share, 0); }); } #[test] fn test_extreme_overflow_protection() { ExtBuilder::build().execute_with(|| { let in_share = Some(50_000_000); // 5% let out_share = Some(100_000_000); // 10% let (chain_id, network) = prepare_network_data(in_share, out_share); register_and_check_network(chain_id, network); let near_max = u128::MAX - 1000; NetworkImbalance::::put(NetworkImbalanceState { outgoing: 0, incoming: near_max, curve_share: 0, }); let massive_amount = 100_000_000; let result = Pallet::::register_incoming(&chain_id, massive_amount); assert!(result.is_err()); GatekeeperAmounts::::insert(&chain_id, 420); let result = Pallet::::register_outgoing(&chain_id, massive_amount); assert!(result.is_err()); }); }