#![cfg(test)] use std::collections::HashMap; use super::*; use crate::evm_types::Log; use crate::mock::*; use frame_support::{assert_err, assert_ok, dispatch}; use sp_core::offchain::{ testing, testing::{TestOffchainExt, TestTransactionPoolExt}, OffchainDbExt, OffchainWorkerExt, TransactionPoolExt, }; use sp_runtime::testing::UintAuthorityId; use ghost_networks::BridgedInflationCurve; use pallet_staking::EraPayout; #[test] fn should_calculate_throttling_slash_function_correctly() { let dummy_offence = SlowClapOffence { session_index: 0, validator_set_count: 69, offenders: vec![()], offence_type: OffenceType::ThrottlingOffence(Perbill::from_percent(50)), }; assert_eq!(dummy_offence.slash_fraction(1), Perbill::from_percent(50)); assert_eq!(dummy_offence.slash_fraction(5), Perbill::from_percent(10)); assert_eq!(dummy_offence.slash_fraction(10), Perbill::from_percent(5)); let dummy_offence = SlowClapOffence { session_index: 0, validator_set_count: 100, offenders: vec![()], offence_type: OffenceType::CommitmentOffence, }; assert_eq!(dummy_offence.slash_fraction(1), Perbill::zero()); assert_eq!(dummy_offence.slash_fraction(5), Perbill::zero()); assert_eq!( dummy_offence.slash_fraction(15), Perbill::from_parts(57_600_000) ); assert_eq!( dummy_offence.slash_fraction(20), Perbill::from_parts(193_600_000) ); assert_eq!( dummy_offence.slash_fraction(25), Perbill::from_parts(409_600_000) ); assert_eq!( dummy_offence.slash_fraction(30), Perbill::from_parts(705_600_000) ); assert_eq!(dummy_offence.slash_fraction(50), Perbill::one()); } #[test] fn bitmap_operations_correct() { let max_value = 440; let sixty_nine = 69; let four_twenty = 420; let mut bitmap = BitMap::new(max_value); assert_eq!(bitmap.max_value(), max_value); assert!(!bitmap.exists(&sixty_nine)); bitmap.set(sixty_nine); assert!(bitmap.exists(&sixty_nine)); assert!(!bitmap.exists(&four_twenty)); bitmap.set(four_twenty); assert!(bitmap.exists(&four_twenty)); let bitmap_cloned = bitmap.clone(); let mut bucket_iter = bitmap_cloned.iter_buckets(); let empty_result = 0; assert_eq!(bucket_iter.next(), Some((&0u32, &empty_result))); // index = 69 / 64 = 1; bit_pos = 69 % 64 = 5 let first_result = 1 << 5; assert_eq!(bucket_iter.next(), Some((&1u32, &first_result))); assert_eq!(bucket_iter.next(), Some((&2u32, &empty_result))); assert_eq!(bucket_iter.next(), Some((&3u32, &empty_result))); assert_eq!(bucket_iter.next(), Some((&4u32, &empty_result))); assert_eq!(bucket_iter.next(), Some((&5u32, &empty_result))); // index = 420 / 64 = 6; bit_pos = 420 % 64 = 36 let second_result = 1 << 36; assert_eq!(bucket_iter.next(), Some((&6u32, &second_result))); assert_eq!(bucket_iter.next(), None); assert_eq!(bitmap.get_bucket(&0), empty_result); assert_eq!(bitmap.get_bucket(&1), first_result); assert_eq!(bitmap.get_bucket(&6), second_result); bitmap.unset(sixty_nine); bitmap.unset(four_twenty); assert!(!bitmap.exists(&sixty_nine)); assert!(!bitmap.exists(&four_twenty)); assert_eq!(BitMap::size_of_bucket(), 6); // for u64 let mut bitmap1 = BitMap::new(max_value); let mut bitmap2 = BitMap::new(max_value); assert_eq!(bitmap1.max_value(), max_value); assert_eq!(bitmap2.max_value(), max_value); bitmap1.set(1); bitmap1.set(12); bitmap1.set(44); bitmap1.set(80); bitmap2.set(1); bitmap2.set(2); bitmap2.set(15); bitmap2.set(36); bitmap2.set(130); let ones = bitmap1.clone().bitor(bitmap2.clone()).count_ones(); assert_eq!(ones, 8); bitmap2.set(80); bitmap2.set(420); let ones = bitmap1.clone().bitor(bitmap2.clone()).count_ones(); assert_eq!(ones, 9); bitmap1.set(15); bitmap1.set(69); bitmap1.set(33); bitmap2.set(15); bitmap2.set(69); bitmap2.set(33); let ones = bitmap1.clone().bitor(bitmap2.clone()).count_ones(); assert_eq!(ones, 11); } #[test] fn request_body_is_correct_for_get_block_number() { let (offchain, _) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); t.execute_with(|| { let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_evm_request_body_for_latest_block(&network_data); assert_eq!( core::str::from_utf8(&request_body).unwrap(), r#"{"id":0,"jsonrpc":"2.0","method":"eth_blockNumber"}"# ); }); } #[test] fn request_body_is_correct_for_get_logs() { let (offchain, _) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); t.execute_with(|| { let from_block: u64 = 420; let to_block: u64 = 1337; let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_evm_request_body_for_latest_transfers( from_block, to_block, &network_data); assert_eq!(core::str::from_utf8(&request_body).unwrap(), r#"{"id":0,"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":"0x1a4","toBlock":"0x539","address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]}"#, ); }); } #[test] fn should_make_http_call_for_block_number() { let (offchain, state) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); evm_block_response(&mut state.write()); let _: Result<(), OffchainErr> = t.execute_with(|| { let rpc_endpoints = get_rpc_endpoints(); let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_evm_request_body_for_latest_block(&network_data); let pending_requests = SlowClap::prepare_pending_evm_requests(&rpc_endpoints, &request_body)?; let raw_responses = SlowClap::fetch_multiple_evm_from_remote(pending_requests); let raw_response = raw_responses.first().unwrap(); assert_eq!(raw_response.len(), 45usize); // precalculated Ok(()) }); } #[test] fn should_make_http_call_for_logs() { let (offchain, state) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); evm_logs_response(&mut state.write()); let _: Result<(), OffchainErr> = t.execute_with(|| { let from_block: u64 = 20335770; let to_block: u64 = 20335858; let rpc_endpoints = get_rpc_endpoints(); let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_evm_request_body_for_latest_transfers( from_block, to_block, &network_data, ); let pending_requests = SlowClap::prepare_pending_evm_requests(&rpc_endpoints, &request_body)?; let raw_responses = SlowClap::fetch_multiple_evm_from_remote(pending_requests); let raw_response = raw_responses.first().unwrap(); assert_eq!(raw_response.len(), 1805); // precalculated Ok(()) }); } #[test] fn should_make_http_call_and_parse_block_number() { let (offchain, state) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); evm_block_response(&mut state.write()); let _: Result<(), OffchainErr> = t.execute_with(|| { let rpc_endpoints = get_rpc_endpoints(); let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_evm_request_body_for_latest_block(&network_data); let pending_requests = SlowClap::prepare_pending_evm_requests(&rpc_endpoints, &request_body)?; let raw_responses = SlowClap::fetch_multiple_evm_from_remote(pending_requests); let raw_response = raw_responses.first().unwrap(); let evm_block_number = SlowClap::parse_evm_response(&raw_response).map( |parsed_response| match parsed_response { EvmResponseType::BlockNumber(block_number) => block_number, EvmResponseType::TransactionLogs(_) => 0, }, )?; assert_eq!(evm_block_number, 20335745); Ok(()) }); } #[test] fn should_make_http_call_and_parse_logs() { let (offchain, state) = TestOffchainExt::new(); let (pool, _) = TestTransactionPoolExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainDbExt::new(offchain.clone())); t.register_extension(OffchainWorkerExt::new(offchain)); t.register_extension(TransactionPoolExt::new(pool)); evm_logs_response(&mut state.write()); let _: Result<(), OffchainErr> = t.execute_with(|| { let rpc_endpoints = get_rpc_endpoints(); let from_block: u64 = 20335770; let to_block: u64 = 20335858; let network_data = prepare_evm_network(None, None); let request_body = SlowClap::prepare_evm_request_body_for_latest_transfers( from_block, to_block, &network_data, ); let pending_requests = SlowClap::prepare_pending_evm_requests(&rpc_endpoints, &request_body)?; let raw_responses = SlowClap::fetch_multiple_evm_from_remote(pending_requests); let raw_response = raw_responses.first().unwrap(); match SlowClap::parse_evm_response(&raw_response)? { EvmResponseType::BlockNumber(_) => assert_eq!(1, 0), // force break EvmResponseType::TransactionLogs(evm_logs) => assert_eq!(evm_logs.len(), 2), } Ok(()) }); } #[test] fn should_prepare_correct_evm_request_for_latest_block() { let (offchain, state) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); evm_block_response(&mut state.write()); let _: Result<(), OffchainErr> = t.execute_with(|| { let rpc_endpoints = get_rpc_endpoints(); let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_evm_request_body_for_latest_block(&network_data); let pending_requests = SlowClap::prepare_pending_evm_requests(&rpc_endpoints, &request_body)?; assert_eq!(pending_requests.len(), rpc_endpoints.len()); for (i, pending_metadata) in pending_requests.iter().enumerate() { assert_eq!(pending_metadata.id.0, i as u16); } Ok(()) }); } #[test] fn should_prepare_correct_evm_request_for_latest_transfers() { let (offchain, state) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); evm_logs_response(&mut state.write()); let _: Result<(), OffchainErr> = t.execute_with(|| { let from_block: u64 = 20335770; let to_block: u64 = 20335858; let rpc_endpoints = get_rpc_endpoints(); let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_evm_request_body_for_latest_transfers( from_block, to_block, &network_data, ); let pending_requests = SlowClap::prepare_pending_evm_requests(&rpc_endpoints, &request_body)?; assert_eq!(pending_requests.len(), rpc_endpoints.len()); for (i, pending_metadata) in pending_requests.iter().enumerate() { assert_eq!(pending_metadata.id.0, i as u16); } Ok(()) }); } #[test] fn should_throw_error_on_empty_prepared_requests() { let (offchain, _) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); let _: Result<(), OffchainErr> = t.execute_with(|| { let rpc_endpoints = vec![]; let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_evm_request_body_for_latest_block(&network_data); assert_err!( SlowClap::prepare_pending_evm_requests(&rpc_endpoints, &request_body), OffchainErr::NoRequestsSent, ); Ok(()) }); } #[test] fn should_ignore_unparsable_rpc_ednpoint() { let (offchain, _) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); let _: Result<(), OffchainErr> = t.execute_with(|| { let rpc_endpoints = vec![get_rpc_endpoint(), vec![0xFF]]; let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_evm_request_body_for_latest_block(&network_data); let pending_empty_requests = SlowClap::prepare_pending_evm_requests(&rpc_endpoints, &request_body)?; assert_eq!(pending_empty_requests.len(), 1); Ok(()) }); } #[test] fn should_fetch_blocks_correctly() { let (offchain, state) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); evm_block_response(&mut state.write()); let _: Result<(), OffchainErr> = t.execute_with(|| { let rpc_endpoints = get_rpc_endpoints(); let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_evm_request_body_for_latest_block(&network_data); let pending_requests = SlowClap::prepare_pending_evm_requests(&rpc_endpoints, &request_body)?; let fetched_blocks_raw = SlowClap::fetch_multiple_evm_from_remote(pending_requests); assert_eq!(fetched_blocks_raw.len(), 2); Ok(()) }); } #[test] fn should_parse_blocks_correctly() { let (offchain, state) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); evm_block_response(&mut state.write()); let _: Result<(), OffchainErr> = t.execute_with(|| { let rpc_endpoints = get_rpc_endpoints(); let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_evm_request_body_for_latest_block(&network_data); let pending_requests_metadata = SlowClap::prepare_pending_evm_requests(&rpc_endpoints, &request_body)?; let parsed_evm_responses = SlowClap::fetch_multiple_evm_from_remote(pending_requests_metadata) .iter() .filter_map(|response_bytes| { let parsed_evm_response = SlowClap::parse_evm_response(response_bytes).ok()?; Some(parsed_evm_response) }) .collect::>(); assert_eq!(parsed_evm_responses.len(), 2); assert_eq!( parsed_evm_responses[0], EvmResponseType::BlockNumber(20335745) ); assert_eq!( parsed_evm_responses[1], EvmResponseType::BlockNumber(20335745) ); Ok(()) }); } #[test] fn should_emit_black_swan_if_not_enough_authorities_left() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); Session::disable_index(2); Session::disable_index(3); advance_session(); advance_session(); System::assert_has_event(RuntimeEvent::SlowClap(crate::Event::BlackSwan)); }); } #[test] fn should_emit_offence_on_missed_clap() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_ok!(do_clap_from(session_index, network_id, 3, false)); advance_session(); advance_session(); System::assert_has_event(RuntimeEvent::SlowClap( crate::Event::SomeAuthoritiesTrottling { throttling: vec![(0, 0)], }, )); }); } #[test] fn should_emit_offence_on_fake_clap() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); advance_session(); advance_session(); System::assert_has_event(RuntimeEvent::SlowClap( crate::Event::SomeAuthoritiesTrottling { throttling: vec![(0, 0), (1, 1)], }, )); }); } #[test] fn should_not_emit_offence_if_already_disabled() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_eq!(Session::disable_index(0), true); advance_session(); advance_session(); System::assert_has_event(RuntimeEvent::SlowClap( crate::Event::SomeAuthoritiesTrottling { throttling: vec![(1, 1)], }, )); }); } #[test] fn should_emit_authorities_equilibrium_if_no_deviation() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_ok!(do_clap_from(session_index, network_id, 3, false)); advance_session(); advance_session(); let session_index = Session::session_index(); for index in 0..10 { let transaction_hash = H256::repeat_byte(index as u8); let receiver: u64 = index + 1; let amount: u64 = 1_000_000_000_000 * receiver; for authority_index in 0..=3 { let clap = Clap { block_number: 1337u64, removed: false, transaction_hash, authority_index, session_index, network_id, receiver, amount, }; let unique_hash = SlowClap::generate_unique_hash(&clap); let authority = UintAuthorityId::from(index as u64); let signature = authority.sign(&clap.encode()).unwrap(); assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)); assert_clapped(&session_index, &unique_hash, authority_index, true); } } advance_session(); advance_session(); let binding = System::events(); let number_of_events = binding .iter() .filter(|x| { x.event == RuntimeEvent::SlowClap(crate::Event::AuthoritiesApplauseEquilibrium) }) .count(); assert_eq!(number_of_events, 6); }); } #[test] fn should_clear_sesions_based_on_history_depth() { let (network_id, _, unique_hash) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let history_depth = HistoryDepth::get(); let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_eq!( ApplauseDetails::::get(&session_index, &unique_hash), None ); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_ok!(do_clap_from(session_index, network_id, 3, false)); assert_eq!( ApplauseDetails::::get(&session_index, &unique_hash).is_some(), true ); for _ in 0..history_depth { advance_session(); } assert_eq!( ApplauseDetails::::get(&session_index, &unique_hash), None ); }); } #[test] fn should_collect_commission_accordingly() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); let (_, _, amount, _) = get_mocked_metadata(); new_test_ext().execute_with(|| { let _ = prepare_evm_network(None, Some(500_000_000)); let session_index = advance_session_and_get_index(); assert_eq!(Networks::accumulated_commission(), 0); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_eq!(Networks::accumulated_commission(), amount.saturating_div(2)); }); } #[test] fn should_increase_gatkeeper_amount_accordingly() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); let (_, _, amount, _) = get_mocked_metadata(); new_test_ext().execute_with(|| { let _ = prepare_evm_network(None, Some(500_000_000)); let session_index = advance_session_and_get_index(); assert_eq!(Networks::gatekeeper_amount(network_id), 0); assert_eq!(Networks::bridged_imbalance().bridged_in, 0); assert_eq!(Networks::bridged_imbalance().bridged_out, 0); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_eq!(Networks::gatekeeper_amount(network_id), amount); assert_eq!( Networks::bridged_imbalance().bridged_in, amount.saturating_div(2) ); assert_eq!(Networks::bridged_imbalance().bridged_out, 0); }); } #[test] fn should_mark_clapped_transaction_when_clap_received() { let (network_id, _, unique_hash) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_clapped(&session_index, &unique_hash, 0, true); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_clapped(&session_index, &unique_hash, 1, true); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_clapped(&session_index, &unique_hash, 2, true); assert_ok!(do_clap_from(session_index, network_id, 3, false)); assert_clapped(&session_index, &unique_hash, 3, true); }); } #[test] fn should_applause_and_take_next_claps() { let (network_id, _, unique_hash) = generate_unique_hash(None, None, None, None, None); let (_, receiver, amount, _) = get_mocked_metadata(); new_test_ext().execute_with(|| { let mut clapped_amount = Default::default(); let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_eq!(Balances::total_balance(&receiver), 0); for i in 0..=3 { assert_ok!(do_clap_from(session_index, network_id, i, false)); let account_id = TestExposureListener::get_account_by_index(i as usize).unwrap_or_default(); clapped_amount = TestExposureListener::get_validator_exposure(&account_id) .saturating_add(clapped_amount); assert_clapped_amount(&session_index, &unique_hash, clapped_amount); } assert_applaused(&session_index, &unique_hash); assert_eq!(Balances::total_balance(&receiver), amount); }); } #[test] fn should_throw_error_on_clap_duplication() { let (network_id, _, unique_hash) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_clapped(&session_index, &unique_hash, 0, true); assert_err!( do_clap_from(session_index, network_id, 0, false), Error::::AlreadyClapped ); assert_clapped(&session_index, &unique_hash, 0, true); }); } #[test] fn should_remove_clap_by_authority() { let (network_id, _, unique_hash) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_ok!(do_clap_from(session_index, network_id, 1, true)); assert_clapped(&session_index, &unique_hash, 0, true); assert_clapped(&session_index, &unique_hash, 1, false); }); } #[test] fn should_throw_error_if_executed_block_number_is_higher() { let (network_id, block_number, unique_hash) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_eq!( ApplauseDetails::::get(session_index, unique_hash), None ); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_ok!(do_clap_from(session_index, network_id, 3, false)); assert_clapped(&session_index, &unique_hash, 0, true); assert_applaused(&session_index, &unique_hash); assert_err!( do_clap_from_at_block(session_index, network_id, 0, block_number - 1), Error::::ExecutedBlockIsHigher, ); }); } #[test] fn should_throw_error_if_validator_disabled_and_ignore_later() { let (network_id, _, unique_hash) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_eq!( ApplauseDetails::::get(&session_index, &unique_hash), None ); assert_eq!(Session::disable_index(0), true); assert_err!( do_clap_from(session_index, network_id, 0, false), Error::::DisabledAuthority, ); assert_eq!( ApplauseDetails::::get(&session_index, &unique_hash), None ); advance_session(); let session_index = session_index + 1; assert_eq!(Session::disable_index(0), true); assert_err!( do_clap_from(session_index, network_id, 0, false), Error::::DisabledAuthority, ); assert_eq!( ApplauseDetails::::get(&session_index, &unique_hash), None ); }); } #[test] fn should_clap_without_applause_on_gatekeeper_amount_overflow() { let big_amount: u64 = u64::MAX; let first_receiver: u64 = 1337; let second_receiver: u64 = 420; let (network_id, block_number, unique_hash) = generate_unique_hash(None, None, Some(first_receiver), Some(big_amount), None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); for authority_index in 0..=3 { let clap = Clap { removed: false, block_number, transaction_hash: H256::repeat_byte(1u8), session_index, authority_index, network_id, receiver: first_receiver, amount: big_amount, }; let authority = UintAuthorityId::from(authority_index as u64); let signature = authority.sign(&clap.encode()).unwrap(); assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)); assert_clapped(&session_index, &unique_hash, authority_index, true); } assert_applaused(&session_index, &unique_hash); assert_eq!(Balances::total_balance(&first_receiver), big_amount); assert_eq!(Balances::total_balance(&second_receiver), 0); for authority_index in 0..=3 { let clap = Clap { block_number, removed: false, transaction_hash: H256::repeat_byte(3u8), session_index, authority_index, network_id, receiver: second_receiver, amount: second_receiver, }; let authority = UintAuthorityId::from(authority_index as u64); let signature = authority.sign(&clap.encode()).unwrap(); assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)); } assert_eq!(Balances::total_balance(&first_receiver), big_amount); assert_eq!(Balances::total_balance(&second_receiver), 0); assert_eq!(Networks::gatekeeper_amount(network_id), big_amount); assert_eq!(Networks::bridged_imbalance().bridged_in, big_amount); assert_eq!(Networks::bridged_imbalance().bridged_out, 0); }); } #[test] fn should_nullify_commission_on_finalize() { let total_staked = 69_000_000; let total_issuance = 100_000_000; let (network_id, _, unique_hash) = generate_unique_hash(None, None, None, None, None); let (_, _, amount, _) = get_mocked_metadata(); new_test_ext().execute_with(|| { let _ = prepare_evm_network(None, Some(1_000_000_000)); let session_index = advance_session_and_get_index(); assert_eq!(Networks::accumulated_commission(), 0); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_eq!(Networks::accumulated_commission(), amount); assert_applaused(&session_index, &unique_hash); assert_eq!( BridgedInflationCurve::::era_payout( total_staked, total_issuance, 0u64 ), (amount, 0u64) ); // precomputed values assert_eq!(Networks::accumulated_commission(), 0); assert_ok!(do_clap_from(session_index, network_id, 3, false)); assert_eq!(Networks::accumulated_commission(), 0); }); } #[test] fn should_avoid_session_overlap_on_mended_session_index() { let (network_id, _, unique_hash) = generate_unique_hash(None, None, None, None, None); let (_, receiver, amount, _) = get_mocked_metadata(); new_test_ext().execute_with(|| { let _ = prepare_evm_network(None, None); let session_index = advance_session_and_get_index(); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_clapped(&session_index, &unique_hash, 0, true); assert_clapped(&session_index, &unique_hash, 1, true); assert_clapped(&session_index, &unique_hash, 2, false); assert_clapped(&session_index, &unique_hash, 3, false); advance_session(); let new_session_index = session_index + 1; assert_ok!(do_clap_from(new_session_index, network_id, 2, false)); assert_eq!(Balances::total_balance(&receiver), amount); assert_eq!( ApplauseDetails::::get(new_session_index, unique_hash), None ); assert_applaused(&session_index, &unique_hash); assert_clapped(&session_index, &unique_hash, 0, true); assert_clapped(&session_index, &unique_hash, 1, true); assert_clapped(&session_index, &unique_hash, 2, true); assert_clapped(&session_index, &unique_hash, 3, false); advance_session(); let new_session_index = new_session_index + 1; assert_ok!(do_clap_from(new_session_index, network_id, 3, false)); assert_clapped(&new_session_index, &unique_hash, 0, false); assert_clapped(&new_session_index, &unique_hash, 1, false); assert_clapped(&new_session_index, &unique_hash, 2, false); assert_clapped(&new_session_index, &unique_hash, 3, true); }); } #[test] fn should_emit_event_on_each_clap_and_on_applause() { let (network_id, block_number, unique_hash) = generate_unique_hash(None, None, None, None, None); let (_, receiver, amount, _) = get_mocked_metadata(); new_test_ext().execute_with(|| { let commission = Perbill::from_parts(100_000_000); // 10% let commission_amount = commission.mul_ceil(amount); let amount_after_commission = amount.saturating_sub(commission_amount); let _ = prepare_evm_network(None, Some(100_000_000)); let session_index = advance_session_and_get_index(); assert_eq!( ApplauseDetails::::get(&session_index, &unique_hash), None ); for index in 0..2 { assert_ok!(do_clap_from(session_index, network_id, index, false)); System::assert_last_event(RuntimeEvent::SlowClap(crate::Event::Clapped { clap_unique_hash: unique_hash, receiver: receiver.clone(), authority_id: index, removed: false, network_id, amount, })); } assert_ok!(do_clap_from(session_index, network_id, 2, false)); let binding = System::events(); let last_five_events = binding.iter().rev().take(5).collect::>(); assert_eq!( last_five_events[0].event, RuntimeEvent::SlowClap(crate::Event::Applaused { received_amount: amount_after_commission, receiver: receiver.clone(), block_number, network_id, }) ); assert_eq!( last_five_events[4].event, RuntimeEvent::SlowClap(crate::Event::Clapped { receiver: receiver.clone(), clap_unique_hash: unique_hash, authority_id: 2, removed: false, network_id, amount, }) ); assert_applaused(&session_index, &unique_hash); assert_ok!(do_clap_from(session_index, network_id, 3, false)); System::assert_last_event(RuntimeEvent::SlowClap(crate::Event::Clapped { receiver: receiver.clone(), clap_unique_hash: unique_hash, authority_id: 3, removed: false, network_id, amount, })); }); } #[test] fn should_not_fail_on_sub_existential_balance() { let (network_id, _, unique_hash) = generate_unique_hash(None, None, None, None, None); let (_, receiver, amount, _) = get_mocked_metadata(); new_test_ext().execute_with(|| { let _ = prepare_evm_network(None, Some(1_000_000_000)); // 100% let session_index = advance_session_and_get_index(); assert_eq!(Networks::accumulated_commission(), 0); assert_eq!(Networks::gatekeeper_amount(network_id), 0); assert_eq!(Networks::bridged_imbalance().bridged_in, 0); assert_eq!(Networks::bridged_imbalance().bridged_out, 0); assert_eq!(Balances::total_balance(&receiver), 0); assert_eq!( ApplauseDetails::::get(session_index, unique_hash), None ); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_eq!(Networks::accumulated_commission(), amount); assert_eq!(Networks::gatekeeper_amount(network_id), amount); assert_eq!(Networks::bridged_imbalance().bridged_in, 0); assert_eq!(Networks::bridged_imbalance().bridged_out, 0); assert_eq!(Balances::total_balance(&receiver), 0); assert_applaused(&session_index, &unique_hash); }); } #[test] fn should_register_block_commitments() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_eq!(BlockCommitments::::get(network_id).len(), 0); let last_stored_block = 69; let current_block = SlowClap::current_block_number(); for i in 0..=3 { assert_ok!(do_block_commitment( session_index, network_id, i, last_stored_block, )); } for i in 0..=3 { assert_err!( do_block_commitment(session_index, network_id, i, last_stored_block), Error::::TimeWentBackwards, ); } let block_commitments = BlockCommitments::::get(network_id); assert_eq!(block_commitments.len(), 4); block_commitments.values().for_each(|details| { assert_eq!(details.last_stored_block, last_stored_block); assert_eq!(details.last_updated, current_block); assert_eq!(details.commits, 1); }); advance_session(); assert_eq!(BlockCommitments::::get(network_id).len(), 0); }); } #[test] fn should_disable_on_commitment_inactivity() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_eq!(BlockCommitments::::get(network_id).len(), 0); System::set_block_number(5 * EpochDuration::get() / 2); let last_stored_block = 69; for i in 0..3 { assert_ok!(do_block_commitment( session_index, network_id, i, last_stored_block, )); } let current_block = System::current_block_number(); SlowClap::on_initialize(current_block); System::assert_has_event(RuntimeEvent::SlowClap( crate::Event::SomeAuthoritiesDelayed { delayed: vec![(3, 3)], }, )); let block_commitments = BlockCommitments::::get(network_id); assert_eq!(block_commitments.get(&3), None); block_commitments.values().for_each(|details| { assert_eq!(details.commits, 1); assert_eq!(details.last_stored_block, last_stored_block); assert_eq!(details.last_updated, current_block); }) }); } #[test] fn should_ignore_commitments_below_latest_executed_block() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); let (_, _, _, block_number) = get_mocked_metadata(); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_eq!(LatestExecutedBlock::::get(network_id), 0); assert_eq!(BlockCommitments::::get(network_id).len(), 0); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_ok!(do_clap_from(session_index, network_id, 3, false)); let latest_executed_block = LatestExecutedBlock::::get(&network_id); assert_eq!(latest_executed_block, block_number); for low_block in 0..=block_number { assert_err!( do_block_commitment(session_index, network_id, 0, low_block), Error::::InnerTimeWentBackwards, ); } let next_block_number = block_number.saturating_add(1); assert_ok!(do_block_commitment( session_index, network_id, 0, next_block_number, )); }); } #[test] fn should_disable_on_commitment_block_deviation() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_eq!(BlockCommitments::::get(network_id).len(), 0); let good_last_stored_block = 9_500_000; let bad_last_stored_block = 9_100_000; let current_block = SlowClap::current_block_number(); for i in 0..3 { assert_ok!(do_block_commitment( session_index, network_id, i, good_last_stored_block, )); } assert_ok!(do_block_commitment( session_index, network_id, 3, bad_last_stored_block, )); let to_initialize = EpochDuration::get() .saturating_div(BLOCK_CHECK_CYCLES) .saturating_mul(2); SlowClap::on_initialize(to_initialize); System::assert_has_event(RuntimeEvent::SlowClap( crate::Event::SomeAuthoritiesDelayed { delayed: vec![(3, 3)], }, )); BlockCommitments::::get(network_id) .iter() .for_each(|(account_id, details)| { let block_to_be_stored = if *account_id == 3 { bad_last_stored_block } else { good_last_stored_block }; assert_eq!(details.commits, 1); assert_eq!(details.last_stored_block, block_to_be_stored); assert_eq!(details.last_updated, current_block); }) }); } #[test] fn should_throw_error_on_fast_commitments() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_eq!(BlockCommitments::::get(network_id).len(), 0); let last_stored_block = 9_500_000; let next_stored_block = 9_600_000; let current_block = SlowClap::current_block_number(); assert_ok!(do_block_commitment( session_index, network_id, 0, last_stored_block, )); assert_err!( do_block_commitment(session_index, network_id, 0, next_stored_block), Error::::TimeWentBackwards, ); BlockCommitments::::get(network_id) .get(&0) .map(|details| { assert_eq!(details.last_stored_block, last_stored_block); assert_eq!(details.last_updated, current_block); assert_eq!(details.commits, 1); }) .expect("authority_index 0 has voted; qed"); System::set_block_number(current_block + BLOCK_COMMITMENT_DELAY); let new_current_block = SlowClap::current_block_number(); assert_ok!(do_block_commitment( session_index, network_id, 0, next_stored_block, )); BlockCommitments::::get(network_id) .get(&0) .map(|details| { assert_eq!(details.last_stored_block, next_stored_block); assert_eq!(details.last_updated, new_current_block); assert_eq!(details.commits, 2); }) .expect("authority_index 0 has voted; qed"); }); } #[test] fn should_not_slash_by_applause_if_disabled_by_commitment() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_eq!(BlockCommitments::::get(network_id).len(), 0); System::set_block_number(5 * EpochDuration::get() / 2); let last_stored_block = 9_500_000; let current_block = SlowClap::current_block_number(); let mut disabled_bitmp = BitMap::new(4); assert_eq!( DisabledAuthorityIndexes::::get(&session_index), Some(disabled_bitmp.clone()) ); Session::disable_index(3); disabled_bitmp.set(3); assert_eq!( DisabledAuthorityIndexes::::get(&session_index), Some(disabled_bitmp.clone()) ); for i in 0..=3 { assert_ok!(do_block_commitment( session_index, network_id, i, last_stored_block, )); } SlowClap::on_initialize(current_block); System::assert_has_event(RuntimeEvent::SlowClap( crate::Event::AuthoritiesCommitmentEquilibrium, )); let block_commitments = BlockCommitments::::get(network_id); assert_eq!( DisabledAuthorityIndexes::::get(&session_index), Some(disabled_bitmp) ); assert_eq!(block_commitments.len(), 4); block_commitments.values().for_each(|details| { assert_eq!(details.last_stored_block, last_stored_block); assert_eq!(details.last_updated, current_block); assert_eq!(details.commits, 1); }); }); } #[test] fn should_not_register_block_commitment_on_old_blocks() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_eq!(BlockCommitments::::get(network_id).len(), 0); let very_old_block = 9_499_999; let last_stored_block = 9_500_000; let current_block = SlowClap::current_block_number(); for i in 0..4 { assert_ok!(do_block_commitment( session_index, network_id, i, last_stored_block, )); } assert_err!( do_block_commitment(session_index, network_id, 3, last_stored_block), Error::::TimeWentBackwards ); assert_err!( do_block_commitment(session_index, network_id, 3, very_old_block), Error::::TimeWentBackwards ); BlockCommitments::::get(network_id) .values() .for_each(|details| { assert_eq!(details.commits, 1); assert_eq!(details.last_stored_block, last_stored_block); assert_eq!(details.last_updated, current_block); }) }); } #[test] fn should_split_commit_slash_between_active_validators() { let (network_id, _, _) = generate_unique_hash(None, None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); prepare_evm_network(None, None); assert_eq!(BlockCommitments::::get(network_id).len(), 0); System::set_block_number(5 * EpochDuration::get() / 2); let last_stored_block = 69; for i in 0..3 { assert_ok!(do_block_commitment( session_index, network_id, i, last_stored_block, )); } let current_block = SlowClap::current_block_number(); let offences = Offences::get(); assert_eq!(offences.len(), 0); SlowClap::on_initialize(current_block); System::assert_has_event(RuntimeEvent::SlowClap( crate::Event::SomeAuthoritiesDelayed { delayed: vec![(3, 3)], }, )); let offences = Offences::get(); assert_eq!(offences.len(), 1); for offence in offences { assert_eq!(offence.0, vec![0, 1, 2]); assert_eq!(offence.1.session_index, session_index); assert_eq!(offence.1.validator_set_count, 4); assert_eq!(offence.1.offenders, vec![(3, 3)]); assert_eq!(offence.1.offence_type, OffenceType::CommitmentOffence); } let block_commitments = BlockCommitments::::get(network_id); assert_eq!(block_commitments.get(&3), None); block_commitments.values().for_each(|details| { assert_eq!(details.last_stored_block, last_stored_block); assert_eq!(details.last_updated, current_block); assert_eq!(details.commits, 1); }); }); } #[test] fn should_check_different_networks_during_on_initialize() { let times = 69; let networks_count = 3; let mut check_commitment_events = HashMap::new(); new_test_ext().execute_with(|| { let _ = advance_session_and_get_index(); for network_id in 0..networks_count { prepare_evm_network(Some(network_id), None); } let expected_events_count: u64 = BLOCK_CHECK_CYCLES .saturating_sub(1) .saturating_mul(networks_count as u64) .saturating_mul(times); let expected_events_count: usize = expected_events_count.try_into().unwrap(); let epochs_passed = times * EpochDuration::get(); for check_block in 0..epochs_passed { SlowClap::on_initialize(check_block); } let binding = System::events(); let total_number_of_events = binding .iter() .filter(|x| { x.event == RuntimeEvent::SlowClap(crate::Event::AuthoritiesCommitmentEquilibrium) }) .count(); binding .iter() .map(|record| record.event.clone()) .for_each(|event| { if let RuntimeEvent::SlowClap(crate::Event::BlockCommitmentsCheck { network_id, .. }) = event { *check_commitment_events.entry(network_id).or_insert(0) += 1; } }); assert_eq!(total_number_of_events, expected_events_count); assert_eq!(check_commitment_events.len() as u32, networks_count); for network_id in 0..networks_count { let network_id_count = check_commitment_events.get(&network_id).unwrap(); let expected_events_count = expected_events_count.saturating_div(networks_count as usize); assert_eq!(*network_id_count, expected_events_count); } }); } #[test] fn should_check_responses_correctly() { new_test_ext().execute_with(|| { let empty_responses = vec![]; assert_err!( SlowClap::check_evm_responses_correctness(&empty_responses), OffchainErr::EmptyResponses, ); let different_responses_types = vec![ EvmResponseType::BlockNumber(60), EvmResponseType::BlockNumber(420), EvmResponseType::TransactionLogs(Default::default()), EvmResponseType::BlockNumber(1337), ]; assert_err!( SlowClap::check_evm_responses_correctness(&different_responses_types), OffchainErr::DifferentEvmResponseTypes, ); let correct_block_responses = vec![ EvmResponseType::BlockNumber(69), EvmResponseType::BlockNumber(420), EvmResponseType::BlockNumber(1337), EvmResponseType::BlockNumber(20335745), ]; assert_ok!(SlowClap::check_evm_responses_correctness( &correct_block_responses )); let correct_log_responses = vec![ EvmResponseType::TransactionLogs(Default::default()), EvmResponseType::TransactionLogs(Default::default()), EvmResponseType::TransactionLogs(Default::default()), EvmResponseType::TransactionLogs(Default::default()), ]; assert_ok!(SlowClap::check_evm_responses_correctness( &correct_log_responses )); }); } #[test] fn should_get_balanced_responses_correctly() { new_test_ext().execute_with(|| { let max_distance = 420; let empty_responses = vec![]; assert_err!( SlowClap::get_balanced_evm_response(&empty_responses, max_distance), OffchainErr::EmptyResponses, ); let correct_block_responses = vec![ EvmResponseType::BlockNumber(69), EvmResponseType::BlockNumber(420), EvmResponseType::BlockNumber(422), EvmResponseType::BlockNumber(1337), ]; let median_block = SlowClap::get_balanced_evm_response(&correct_block_responses, max_distance) .expect("median block should be extractable; qed"); assert_eq!(median_block, EvmResponseType::BlockNumber(421)); let contradictory_block_responses = vec![ EvmResponseType::BlockNumber(69), EvmResponseType::BlockNumber(1337), ]; assert_err!( SlowClap::get_balanced_evm_response(&contradictory_block_responses, max_distance), OffchainErr::ContradictoryBlockMedian(1337, 69, max_distance), ); let first_correct_log = Log { transaction_hash: Some(H256::random()), block_number: Some(69), topics: vec![], removed: false, }; let second_correct_log = Log { transaction_hash: Some(H256::random()), block_number: Some(420), topics: vec![], removed: false, }; let first_wrong_block_log = Log { block_number: Some(1338), ..first_correct_log.clone() }; let first_wrong_transaction_log = Log { transaction_hash: Some(H256::zero()), ..first_correct_log.clone() }; let second_wrong_block_log = Log { block_number: Some(1338), ..second_correct_log.clone() }; let second_wrong_transaction_log = Log { transaction_hash: Some(H256::zero()), ..second_correct_log.clone() }; let correct_log_responses = vec![ EvmResponseType::TransactionLogs(vec![ first_correct_log.clone(), second_correct_log.clone(), ]), EvmResponseType::TransactionLogs(vec![ first_correct_log.clone(), second_correct_log.clone(), ]), EvmResponseType::TransactionLogs(vec![ first_correct_log.clone(), second_wrong_block_log.clone(), ]), EvmResponseType::TransactionLogs(vec![ first_correct_log.clone(), second_wrong_transaction_log.clone(), ]), EvmResponseType::TransactionLogs(vec![ first_wrong_block_log.clone(), second_correct_log.clone(), ]), EvmResponseType::TransactionLogs(vec![ first_wrong_transaction_log.clone(), second_correct_log.clone(), ]), EvmResponseType::TransactionLogs(vec![first_wrong_block_log, second_wrong_block_log]), EvmResponseType::TransactionLogs(vec![ first_wrong_transaction_log, second_wrong_transaction_log, ]), ]; let best_logs = SlowClap::get_balanced_evm_response(&correct_log_responses, 420) .expect("best logs should be extractable; qed"); assert_eq!( best_logs, EvmResponseType::TransactionLogs(vec![first_correct_log, second_correct_log]) ); }); } fn assert_clapped_amount( session_index: &SessionIndex, unique_hash: &H256, clapped_amount: BalanceOf, ) { let maybe_details = ApplauseDetails::::get(session_index, unique_hash); assert!(maybe_details.is_some()); let actual_clapped_amount = maybe_details .map(|details| details.clapped_amount) .unwrap_or_default(); assert_eq!(actual_clapped_amount, clapped_amount); } fn assert_applaused(session_index: &SessionIndex, unique_hash: &H256) { let maybe_details = ApplauseDetails::::get(session_index, unique_hash); assert!(maybe_details.is_some()); let is_applaused = maybe_details .map(|details| details.finalized) .unwrap_or_default(); assert!(is_applaused); } fn prepare_evm_network( maybe_network_id: Option, maybe_incoming_commission: Option, ) -> NetworkData { let network_data = NetworkData { chain_name: "Ethereum".into(), default_endpoints: get_rpc_endpoints(), finality_delay: 69, avg_block_speed: 69, rate_limit_delay: 69, block_distance: 69, network_type: ghost_networks::NetworkType::Evm, gatekeeper: get_gatekeeper(), topic_name: get_topic_name(), incoming_fee: maybe_incoming_commission.unwrap_or_default(), outgoing_fee: 0, }; assert_ok!(Networks::register_network( RuntimeOrigin::root(), maybe_network_id.unwrap_or(1u32), network_data.clone() )); network_data } fn do_block_commitment( session_index: u32, network_id: u32, authority_index: u32, last_stored_block: ExternalBlockNumber, ) -> dispatch::DispatchResult { let block_commitment = BlockCommitment { session_index, authority_index, network_id, last_stored_block, }; let authority = UintAuthorityId::from(authority_index as u64); let signature = authority.sign(&block_commitment.encode()).unwrap(); SlowClap::pre_dispatch(&crate::Call::commit_block { block_commitment: block_commitment.clone(), signature: signature.clone(), }) .map_err(|e| <&'static str>::from(e))?; SlowClap::commit_block(RuntimeOrigin::none(), block_commitment, signature) } fn do_clap_from( session_index: u32, network_id: u32, authority_index: u32, transaction_removed: bool, ) -> dispatch::DispatchResult { let (transaction_hash, receiver, amount, block_number) = get_mocked_metadata(); let clap = Clap { block_number, removed: transaction_removed, transaction_hash, session_index, authority_index, network_id, receiver, amount, }; let authority = UintAuthorityId::from(authority_index as u64); let signature = authority.sign(&clap.encode()).unwrap(); SlowClap::pre_dispatch(&crate::Call::slow_clap { clap: clap.clone(), signature: signature.clone(), }) .map_err(|e| <&'static str>::from(e))?; SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature) } fn do_clap_from_at_block( session_index: u32, network_id: u32, authority_index: u32, block_number: u64, ) -> dispatch::DispatchResult { let transaction_hash = H256::repeat_byte(96u8); let receiver: u64 = 1337; let amount: u64 = 420_000_000_000_000; let clap = Clap { block_number, removed: false, transaction_hash, session_index, authority_index, network_id, receiver, amount, }; let authority = UintAuthorityId::from(authority_index as u64); let signature = authority.sign(&clap.encode()).unwrap(); SlowClap::pre_dispatch(&crate::Call::slow_clap { clap: clap.clone(), signature: signature.clone(), }) .map_err(|e| <&'static str>::from(e))?; SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature) } fn assert_clapped( session_index: &SessionIndex, unique_hash: &H256, authority_index: u32, is_clapped: bool, ) { let maybe_details = ApplauseDetails::::get(session_index, unique_hash); assert!(maybe_details.is_some()); let bitmap = maybe_details.map(|details| details.authorities).unwrap(); assert_eq!(bitmap.exists(&authority_index), is_clapped); } fn advance_session_and_get_index() -> u32 { advance_session(); assert_eq!(Session::validators(), Vec::::new()); advance_session(); Session::session_index() } fn generate_unique_hash( maybe_network_id: Option, maybe_transaction_hash: Option, maybe_receiver: Option, maybe_amount: Option, maybe_block_number: Option, ) -> (NetworkIdOf, u64, H256) { let network_id = maybe_network_id.unwrap_or(1); let (transaction_hash, receiver, amount, block_number) = get_mocked_metadata(); let transaction_hash = maybe_transaction_hash.unwrap_or(transaction_hash); let receiver = maybe_receiver.unwrap_or(receiver); let amount = maybe_amount.unwrap_or(amount); let block_number = maybe_block_number.unwrap_or(block_number); let clap = Clap { session_index: 0, authority_index: 0, removed: false, block_number, transaction_hash, network_id, receiver, amount, }; let unique_hash = SlowClap::generate_unique_hash(&clap); (network_id, block_number, unique_hash) } fn get_rpc_endpoint() -> Vec { b"https://rpc.endpoint.network.com".to_vec() } fn get_rpc_endpoints() -> Vec> { vec![ get_rpc_endpoint(), b"https://other.endpoint.network.com".to_vec(), ] } fn get_gatekeeper() -> Vec { b"0x4d224452801ACEd8B2F0aebE155379bb5D594381".to_vec() } fn get_topic_name() -> Vec { b"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef".to_vec() } fn get_mocked_metadata() -> (H256, u64, u64, u64) { let transaction_hash = H256::repeat_byte(69u8); let receiver: u64 = 1337; let amount: u64 = 420_000_000_000_000; let block_number: u64 = 555; (transaction_hash, receiver, amount, block_number) } fn evm_block_response(state: &mut testing::OffchainState) { let expected_body = br#"{"id":0,"jsonrpc":"2.0","method":"eth_blockNumber"}"#.to_vec(); state.expect_request(testing::PendingRequest { method: "POST".into(), uri: "https://rpc.endpoint.network.com".into(), headers: vec![ ("Accept".to_string(), "application/json".to_string()), ("Content-Type".to_string(), "application/json".to_string()), ("User-Agent".to_string(), "curl/8.9.0".to_string()), ], response: Some(b"{\"id\":0,\"jsonrpc\":\"2.0\",\"result\":\"0x1364c81\"}".to_vec()), body: expected_body.clone(), sent: true, ..Default::default() }); state.expect_request(testing::PendingRequest { method: "POST".into(), uri: "https://other.endpoint.network.com".into(), headers: vec![ ("Accept".to_string(), "application/json".to_string()), ("Content-Type".to_string(), "application/json".to_string()), ("User-Agent".to_string(), "curl/8.9.0".to_string()), ], response: Some(b"{\"id\":0,\"jsonrpc\":\"2.0\",\"result\":\"0x1364c81\"}".to_vec()), body: expected_body, sent: true, ..Default::default() }); } fn evm_logs_response(state: &mut testing::OffchainState) { let expected_body = br#"{"id":0,"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":"0x1364c9a","toBlock":"0x1364cf2","address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]}"#.to_vec(); let expected_response = br#"{ "id":0, "jsonrpc":"2.0", "result":[ { "address":"0x4d224452801aced8b2f0aebe155379bb5d594381", "topics":[ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x000000000000000000000000d91efec7e42f80156d1d9f660a69847188950747", "0x0000000000000000000000005e611dfbe71b988cf2a3e5fe4191b5e3d4c4212a" ], "data":"0x000000000000000000000000000000000000000000000046f0d522a71fdac000", "blockNumber":"0x1364c9d", "transactionHash":"0xa82f2fe87f4ba01ab3bd2cd4d0fe75a26f0e9a37e2badc004a0e38f9d088a758", "transactionIndex":"0x11", "blockHash":"0x4b08f82d5a948c0bc5c23c8ddce55e5b4536d7454be53668bbd985ef13dca04a", "logIndex":"0x92", "removed":false }, { "address":"0x4d224452801aced8b2f0aebe155379bb5d594381", "topics":[ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000005954ab967bc958940b7eb73ee84797dc8a2afbb9", "0x000000000000000000000000465165be7cacdbd2cbc8334f549fab9783ad6e7a" ], "data":"0x0000000000000000000000000000000000000000000000027c790defec959e75", "blockNumber":"0x1364cf1", "transactionHash":"0xd9f02b79a90db7536b0afb5e24bbc1f4319dc3d8c57af7c39941acd249ec053a", "transactionIndex":"0x2c", "blockHash":"0x6876b18f5081b5d24d5a73107a667a7713256f6e51edbbe52bf8df24e340b0f7", "logIndex":"0x14b", "removed":false } ] }"#.to_vec(); state.expect_request(testing::PendingRequest { method: "POST".into(), uri: "https://rpc.endpoint.network.com".into(), headers: vec![ ("Accept".to_string(), "application/json".to_string()), ("Content-Type".to_string(), "application/json".to_string()), ("User-Agent".to_string(), "curl/8.9.0".to_string()), ], response_headers: vec![ ("Accept".to_string(), "application/json".to_string()), ("Content-Type".to_string(), "application/json".to_string()), ], body: expected_body.clone(), response: Some(expected_response.clone()), sent: true, ..Default::default() }); state.expect_request(testing::PendingRequest { method: "POST".into(), uri: "https://other.endpoint.network.com".into(), headers: vec![ ("Accept".to_string(), "application/json".to_string()), ("Content-Type".to_string(), "application/json".to_string()), ("User-Agent".to_string(), "curl/8.9.0".to_string()), ], response_headers: vec![ ("Accept".to_string(), "application/json".to_string()), ("Content-Type".to_string(), "application/json".to_string()), ], body: expected_body, response: Some(expected_response), sent: true, ..Default::default() }); }