#![cfg(test)] use super::*; use crate::mock::*; use frame_support::{assert_err, assert_ok, dispatch}; use sp_core::offchain::{ testing, testing::{TestOffchainExt, TestTransactionPoolExt}, OffchainWorkerExt, OffchainDbExt, TransactionPoolExt, }; use sp_runtime::testing::UintAuthorityId; use ghost_networks::BridgedInflationCurve; use pallet_staking::EraPayout; const MAX_DEVIATION_DEPTH: u32 = 10; fn prepare_evm_network( maybe_network_id: Option, maybe_incoming_commission: Option, ) -> NetworkData { let network_data = NetworkData { chain_name: "Ethereum".into(), default_endpoint: get_rpc_endpoint(), finality_delay: Some(69), release_delay: Some(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_clap_from( session_index: u32, network_id: u32, authority_index: u32, transaction_removed: bool, ) -> dispatch::DispatchResult { let (transaction_hash, receiver, amount) = get_mocked_metadata(); let clap = Clap { block_number: 420, removed: transaction_removed, transaction_hash, session_index, authority_index, network_id, receiver, amount, }; let authority = UintAuthorityId::from((authority_index + 1) 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) } #[test] fn test_throttling_slash_function() { let dummy_offence = ThrottlingOffence { session_index: 0, validator_set_count: 50, offenders: vec![()], }; assert_eq!(dummy_offence.slash_fraction(1), Perbill::zero()); assert_eq!(dummy_offence.slash_fraction(5), Perbill::zero()); assert_eq!(dummy_offence.slash_fraction(7), Perbill::from_parts(4200000)); assert_eq!(dummy_offence.slash_fraction(17), Perbill::from_parts(46200000)); } #[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_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_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()); t.execute_with(|| { let rpc_endpoint = get_rpc_endpoint(); let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_request_body_for_latest_block(&network_data); let raw_response = SlowClap::fetch_from_remote(&rpc_endpoint, &request_body).unwrap(); assert_eq!(raw_response.len(), 45usize); // precalculated }); } #[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()); t.execute_with(|| { let from_block: u64 = 420; let to_block: u64 = 1337; let rpc_endpoint = get_rpc_endpoint(); let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_request_body_for_latest_transfers( from_block, to_block, &network_data); let raw_response = SlowClap::fetch_from_remote(&rpc_endpoint, &request_body) .unwrap(); assert_eq!(raw_response.len(), 1805); // precalculated }); } #[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()); t.execute_with(|| { let rpc_endpoint = get_rpc_endpoint(); let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_request_body_for_latest_block(&network_data); let raw_response = SlowClap::fetch_from_remote(&rpc_endpoint, &request_body).unwrap(); let evm_block_number = SlowClap::apply_evm_response( &raw_response, 69, Default::default(), 420, 1).unwrap_or_default(); assert_eq!(evm_block_number, 20335745); }); } #[test] fn should_make_http_call_and_parse_logs() { let (offchain, state) = TestOffchainExt::new(); let (pool, _state) = 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()); t.execute_with(|| { let from_block: u64 = 420; let to_block: u64 = 1337; let rpc_endpoint = get_rpc_endpoint(); let network_data = prepare_evm_network(Some(1), None); let request_body = SlowClap::prepare_request_body_for_latest_transfers( from_block, to_block, &network_data); let raw_response = SlowClap::fetch_from_remote(&rpc_endpoint, &request_body).unwrap(); let evm_block_number = SlowClap::apply_evm_response( &raw_response, 69, Default::default(), 420, 1).unwrap_or(20335745); assert_eq!(evm_block_number, 0); }); } #[test] fn should_collect_commission_accordingly() { let (network_id, _, _) = generate_unique_hash(None, None, None, None); let (_, _, amount) = get_mocked_metadata(); new_test_ext().execute_with(|| { let _ = prepare_evm_network(Some(network_id), 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); let (_, _, amount) = get_mocked_metadata(); new_test_ext().execute_with(|| { let _ = prepare_evm_network(Some(network_id), 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.saturating_div(2)); 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, transaction_hash, unique_transaction_hash) = generate_unique_hash(None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); let storage_key = (session_index, transaction_hash, unique_transaction_hash); assert_claps_info_correct(&storage_key, &session_index, 0); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_claps_info_correct(&storage_key, &session_index, 1); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_claps_info_correct(&storage_key, &session_index, 2); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_claps_info_correct(&storage_key, &session_index, 3); }); } #[test] fn should_applause_and_take_next_claps() { let (network_id, transaction_hash, unique_transaction_hash) = generate_unique_hash(None, None, None, None); let (_, receiver, amount) = get_mocked_metadata(); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); let storage_key = (session_index, transaction_hash, unique_transaction_hash); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), false); assert_eq!(Balances::balance(&receiver), 0); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), false); assert_eq!(Balances::balance(&receiver), 0); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), true); assert_eq!(Balances::balance(&receiver), amount); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), true); assert_eq!(Balances::balance(&receiver), amount); }); } #[test] fn should_throw_error_on_clap_duplication() { let (network_id, transaction_hash, unique_transaction_hash) = generate_unique_hash(None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); let storage_key = (session_index, transaction_hash, unique_transaction_hash); assert_claps_info_correct(&storage_key, &session_index, 0); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_claps_info_correct(&storage_key, &session_index, 1); assert_err!(do_clap_from(session_index, network_id, 0, false), Error::::AlreadyClapped); assert_claps_info_correct(&storage_key, &session_index, 1); }); } #[test] fn should_throw_error_on_removal_of_unregistered_clap() { let (network_id, transaction_hash, unique_transaction_hash) = generate_unique_hash(None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); let storage_key = (session_index, transaction_hash, unique_transaction_hash); assert_claps_info_correct(&storage_key, &session_index, 0); assert_err!(do_clap_from(session_index, network_id, 0, true), Error::::UnregisteredClapRemove); assert_claps_info_correct(&storage_key, &session_index, 0); }); } #[test] fn should_throw_error_if_session_index_is_not_current() { let (network_id, transaction_hash, unique_transaction_hash) = generate_unique_hash(None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); for deviation in 1..MAX_DEVIATION_DEPTH { let session_index_up = session_index.saturating_add(deviation); let session_index_down = session_index.saturating_sub(deviation); let storage_key_up = (session_index_up, transaction_hash, unique_transaction_hash); let storage_key_down = (session_index_down, transaction_hash, unique_transaction_hash); assert_claps_info_correct(&storage_key_up, &session_index, 0); assert_claps_info_correct(&storage_key_down, &session_index, 0); assert_err!(do_clap_from(session_index_up, network_id, 0, false), Error::::ClapForWrongSession); assert_err!(do_clap_from(session_index_down, network_id, 0, false), Error::::ClapForWrongSession); assert_claps_info_correct(&storage_key_up, &session_index, 0); assert_claps_info_correct(&storage_key_down, &session_index, 0); } }); } #[test] fn should_throw_error_if_signer_has_incorrect_index() { let (network_id, transaction_hash, unique_transaction_hash) = generate_unique_hash(None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); let storage_key = (session_index, transaction_hash, unique_transaction_hash); assert_claps_info_correct(&storage_key, &session_index, 0); let clap = Clap { block_number: 420, removed: false, transaction_hash, session_index, authority_index: 1337, network_id, receiver: 69, amount: 420, }; let authority = UintAuthorityId::from((1) as u64); let signature = authority.sign(&clap.encode()).unwrap(); assert_err!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature), Error::::NotAnAuthority); assert_claps_info_correct(&storage_key, &session_index, 0); }); } #[test] fn should_throw_error_if_validator_disabled_and_ignore_later() { let (network_id, transaction_hash, unique_transaction_hash) = generate_unique_hash(None, None, None, None); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); let storage_key = (session_index, transaction_hash, unique_transaction_hash); assert_claps_info_correct(&storage_key, &session_index, 0); assert_eq!(Session::disable_index(0), true); assert_err!(do_clap_from(session_index, network_id, 0, false), Error::::CurrentValidatorIsDisabled); assert_eq!(pallet::ReceivedClaps::::get(&storage_key).len(), 0); assert_eq!(pallet::ClapsInSession::::get(&session_index).len(), 1); assert_eq!(pallet::ClapsInSession::::get(&session_index) .into_iter() .filter(|(_, v)| !v.disabled) .collect::>() .len(), 0); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_eq!(Session::disable_index(1), true); assert_eq!(pallet::ReceivedClaps::::get(&storage_key).len(), 1); assert_eq!(pallet::ClapsInSession::::get(&session_index).len(), 2); assert_eq!(pallet::ClapsInSession::::get(&session_index) .into_iter() .filter(|(_, v)| !v.disabled) .collect::>() .len(), 0); }); } #[test] fn should_throw_error_on_gatekeeper_amount_overflow() { let big_amount: u64 = u64::MAX; let first_receiver: u64 = 1337; let second_receiver: u64 = 420; let (network_id, transaction_hash, unique_transaction_hash) = generate_unique_hash(None, None, Some(first_receiver), Some(big_amount)); new_test_ext().execute_with(|| { let session_index = advance_session_and_get_index(); let storage_key_first = (session_index, transaction_hash, unique_transaction_hash); for authority_index in 0..=2 { let clap = Clap { block_number: 420, removed: false, transaction_hash, session_index, authority_index, network_id, receiver: first_receiver, amount: big_amount, }; let authority = UintAuthorityId::from((authority_index + 1) as u64); let signature = authority.sign(&clap.encode()).unwrap(); assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)); } assert_claps_info_correct(&storage_key_first, &session_index, 3); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key_first), true); assert_eq!(Balances::balance(&first_receiver), big_amount); assert_eq!(Balances::balance(&second_receiver), 0); for authority_index in 0..=2 { let clap = Clap { block_number: 420, removed: false, transaction_hash: H256::repeat_byte(3u8), session_index, authority_index, network_id, receiver: second_receiver, amount: 420, }; let authority = UintAuthorityId::from((authority_index + 1) as u64); let signature = authority.sign(&clap.encode()).unwrap(); if authority_index == 0 { assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)); } else { assert_err!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature), Error::::CouldNotIncreaseGatekeeperAmount); } } assert_eq!(Balances::balance(&first_receiver), big_amount); assert_eq!(Balances::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_throw_error_on_commission_overflow() { let big_amount: u64 = u64::MAX; let first_receiver: u64 = 1337; let second_receiver: u64 = 420; let network_id_other: u32 = 69; let (network_id, transaction_hash, unique_transaction_hash) = generate_unique_hash(None, None, Some(first_receiver), Some(big_amount)); new_test_ext().execute_with(|| { let _ = prepare_evm_network(Some(network_id), Some(0)); let _ = prepare_evm_network(Some(network_id_other), Some(0)); let session_index = advance_session_and_get_index(); let storage_key_first = (session_index, transaction_hash, unique_transaction_hash); for authority_index in 0..=2 { let clap = Clap { block_number: 420, removed: false, transaction_hash, session_index, authority_index, network_id, receiver: first_receiver, amount: big_amount, }; let authority = UintAuthorityId::from((authority_index + 1) as u64); let signature = authority.sign(&clap.encode()).unwrap(); assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)); } assert_claps_info_correct(&storage_key_first, &session_index, 3); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key_first), true); assert_eq!(Balances::balance(&first_receiver), big_amount); assert_eq!(Balances::balance(&second_receiver), 0); for authority_index in 0..=2 { let clap = Clap { block_number: 420, removed: false, transaction_hash: H256::repeat_byte(3u8), session_index, authority_index, network_id: network_id_other, receiver: 420, amount: big_amount, }; let authority = UintAuthorityId::from((authority_index + 1) as u64); let signature = authority.sign(&clap.encode()).unwrap(); if authority_index == 0 { assert_ok!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature)); } else { assert_err!(SlowClap::slow_clap(RuntimeOrigin::none(), clap, signature), Error::::CouldNotIncreaseGatekeeperAmount); } } assert_eq!(Balances::balance(&first_receiver), big_amount); assert_eq!(Balances::balance(&second_receiver), 0); assert_eq!(Networks::gatekeeper_amount(network_id), big_amount); assert_eq!(Networks::gatekeeper_amount(network_id_other), 0); 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, _, _) = generate_unique_hash(None, None, None, None); let (_, _, amount) = get_mocked_metadata(); new_test_ext().execute_with(|| { let _ = prepare_evm_network(Some(network_id), 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_eq!(Networks::accumulated_commission(), amount.saturating_div(2)); assert_eq!(Networks::is_nullification_period(), false); assert_eq!(BridgedInflationCurve::::era_payout( total_staked, (total_issuance + amount).into(), 0), (1260099399952u128, 208739900600048u128)); // precomputed values assert_eq!(Networks::is_nullification_period(), true); Networks::on_finalize(System::block_number()); assert_eq!(Networks::is_nullification_period(), false); assert_eq!(Networks::accumulated_commission(), 0); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_eq!(Networks::accumulated_commission(), 0); }); } #[test] fn should_avoid_applause_during_nullification_period() { let total_staked = 69_000_000; let total_issuance = 100_000_000; let (network_id, _, _) = generate_unique_hash(None, None, None, None); let (_, receiver, amount) = get_mocked_metadata(); new_test_ext().execute_with(|| { let _ = prepare_evm_network(Some(network_id), Some(0)); let session_index = advance_session_and_get_index(); assert_eq!(Networks::is_nullification_period(), false); assert_eq!(BridgedInflationCurve::::era_payout( total_staked, total_issuance, 0), (0, 0)); assert_eq!(Networks::is_nullification_period(), true); assert_ok!(do_clap_from(session_index, network_id, 0, false)); assert_ok!(do_clap_from(session_index, network_id, 1, false)); assert_eq!(Balances::balance(&receiver), 0); Networks::on_finalize(System::block_number()); assert_eq!(Networks::is_nullification_period(), false); assert_ok!(do_clap_from(session_index, network_id, 2, false)); assert_eq!(Balances::balance(&receiver), amount); }); } #[test] fn should_self_applause_if_enough_received_claps() { let (network_id, transaction_hash, unique_transaction_hash) = generate_unique_hash(None, None, None, None); let (_, receiver, amount) = get_mocked_metadata(); new_test_ext().execute_with(|| { let _ = prepare_evm_network(Some(network_id), Some(0)); let session_index = advance_session_and_get_index(); let storage_key = (session_index, transaction_hash, unique_transaction_hash); assert_err!(SlowClap::self_applause( RuntimeOrigin::signed(receiver), network_id, session_index, transaction_hash, receiver, amount, ), Error::::NotEnoughClaps); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), false); assert_eq!(Balances::balance(&receiver), 0); assert_eq!(BridgedInflationCurve::::era_payout( 0, 0, 0), (0, 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!(pallet::ApplausesForTransaction::::get(&storage_key), false); assert_eq!(Balances::balance(&receiver), 0); assert_ok!(SlowClap::self_applause( RuntimeOrigin::signed(receiver), network_id, session_index, transaction_hash, receiver, amount, )); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), false); assert_eq!(Balances::balance(&receiver), 0); Networks::on_finalize(System::block_number()); assert_ok!(SlowClap::self_applause( RuntimeOrigin::signed(receiver), network_id, session_index, transaction_hash, receiver, amount, )); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), true); assert_eq!(Balances::balance(&receiver), amount); }); } #[test] fn should_emit_event_on_each_clap_and_on_applause() { let (network_id, transaction_hash, unique_transaction_hash) = generate_unique_hash(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(Some(network_id), Some(100_000_000)); let session_index = advance_session_and_get_index(); let storage_key = (session_index, transaction_hash, unique_transaction_hash); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), false); assert_claps_info_correct(&storage_key, &session_index, 0); assert_ok!(do_clap_from(session_index, network_id, 0, false)); System::assert_last_event(RuntimeEvent::SlowClap( crate::Event::Clapped { receiver: receiver.clone(), authority_id: 0, network_id, transaction_hash, amount, })); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), false); assert_claps_info_correct(&storage_key, &session_index, 1); assert_ok!(do_clap_from(session_index, network_id, 1, false)); let binding = System::events(); let last_two_events = binding .iter() .rev() .take(5) .collect::>(); assert_eq!(last_two_events[0].event, RuntimeEvent::SlowClap( crate::Event::Applaused { network_id, receiver: receiver.clone(), received_amount: amount_after_commission, })); assert_eq!(last_two_events[4].event, RuntimeEvent::SlowClap( crate::Event::Clapped { receiver: receiver.clone(), authority_id: 1, network_id, transaction_hash, amount, })); assert_eq!(pallet::ApplausesForTransaction::::get(&storage_key), true); assert_claps_info_correct(&storage_key, &session_index, 2); assert_ok!(do_clap_from(session_index, network_id, 2, false)); System::assert_last_event(RuntimeEvent::SlowClap( crate::Event::Clapped { receiver: receiver.clone(), authority_id: 2, network_id, transaction_hash, amount, })); }); } // TODO: multiple logs will create multiple records // TODO: errors should be checked as much as possible // TODO: offences generated as expected // TODO: deal with below existential amount after commission 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, ) -> (u32, H256, H256) { let network_id = maybe_network_id.unwrap_or(1); let (transaction_hash, receiver, amount) = 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 unique_transaction_hash = SlowClap::generate_unique_hash( &receiver, &amount, &network_id, ); (network_id, transaction_hash, unique_transaction_hash) } fn assert_claps_info_correct( storage_key: &(u32, H256, H256), session_index: &u32, index: usize, ) { assert_eq!(pallet::ReceivedClaps::::get(storage_key).len(), index); assert_eq!(pallet::ClapsInSession::::get(session_index).len(), index); } fn get_rpc_endpoint() -> Vec { b"https://rpc.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) { let transaction_hash = H256::repeat_byte(69u8); let receiver: u64 = 1337; let amount: u64 = 420_000_000_000_000; (transaction_hash, receiver, amount) } 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()), ], 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":"0x1a4","toBlock":"0x539","address":"0x4d224452801ACEd8B2F0aebE155379bb5D594381","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}]}"#.to_vec(); let expected_response = br#"{ "jsonrpc":"2.0", "id":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()), ], body: expected_body, response: Some(expected_response), sent: true, ..Default::default() }); }