#![cfg(test)] use super::*; use crate::mock::*; use frame_support::{assert_err, assert_ok}; use sp_core::offchain::{ testing, testing::TestOffchainExt, OffchainWorkerExt, // OffchainDbExt, TransactionPoolExt, }; // use sp_runtime::testing::UintAuthorityId; fn prepare_networks() -> (u32, u32) { let network = 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: 0, outgoing_fee: 0, }; assert_ok!(Networks::register_network( RuntimeOrigin::root(), get_network_id(), network)); (1, 69) } fn prepare_companion(amount: u64) -> Companion, BalanceOf> { Companion { network_id: 1, receiver: H160::from_low_u64_ne(1337), fee: H256::from_low_u64_ne(40_000), amount: amount, } } #[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 propose_companion_works_as_expected() { new_test_ext().execute_with(|| { let actor = alice_account_id(); let companion_id = SlowClap::current_companion_id(); let (valid_network_id, _) = prepare_networks(); let companion = prepare_companion(100); assert_eq!(Balances::balance(&actor), 100); assert_eq!(Balances::total_issuance(), 100); assert_eq!(SlowClap::companions(valid_network_id, companion_id), None); assert_eq!(SlowClap::companion_details(companion_id), None); assert_eq!(SlowClap::current_companion_id(), companion_id); assert_ok!(SlowClap::propose_companion(RuntimeOrigin::signed(actor), valid_network_id, companion.clone())); assert_eq!(Balances::balance(&actor), 0); assert_eq!(Balances::total_issuance(), 0); assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor)); assert_eq!(SlowClap::companion_details(companion_id), Some(companion)); assert_eq!(SlowClap::current_companion_id(), companion_id + 1); }); } #[test] fn propose_companion_emits_event() { new_test_ext().execute_with(|| { let actor = alice_account_id(); let companion_id = SlowClap::current_companion_id(); let (valid_network_id, _) = prepare_networks(); let companion = prepare_companion(100); System::reset_events(); assert_ok!(SlowClap::propose_companion( RuntimeOrigin::signed(actor), valid_network_id, companion.clone())); System::assert_has_event(RuntimeEvent::SlowClap( crate::Event::CompanionCreated { companion_id, owner: actor, companion, })); }); } #[test] fn could_not_propose_if_not_enough_funds() { new_test_ext().execute_with(|| { let actor = alice_account_id(); let (valid_network_id, _) = prepare_networks(); let companion = prepare_companion(1_000); assert_err!(SlowClap::propose_companion( RuntimeOrigin::signed(actor), valid_network_id, companion.clone()), sp_runtime::TokenError::FundsUnavailable); let companion = prepare_companion(100 / 2); let prev_balance = Balances::balance(&actor); assert_ok!(SlowClap::propose_companion( RuntimeOrigin::signed(actor), valid_network_id, companion.clone())); assert_eq!(Balances::balance(&actor), prev_balance / 2); }); } #[test] fn companion_amount_should_be_existent() { new_test_ext().execute_with(|| { let actor = alice_account_id(); let (valid_network_id, _) = prepare_networks(); let companion = prepare_companion(1); assert_err!(SlowClap::propose_companion( RuntimeOrigin::signed(actor), valid_network_id, companion.clone()), crate::Error::::CompanionAmountNotExistent); }); } #[test] fn could_not_register_companion_if_network_not_registered() { new_test_ext().execute_with(|| { let actor = alice_account_id(); let (_, invalid_network_id) = prepare_networks(); let companion = prepare_companion(100); assert_err!(SlowClap::propose_companion( RuntimeOrigin::signed(actor), invalid_network_id, companion.clone()), crate::Error::::NonRegisteredNetwork); }); } #[test] fn release_companion_works() { new_test_ext().execute_with(|| { let actor = alice_account_id(); let companion_id = SlowClap::current_companion_id(); let (valid_network_id, _) = prepare_networks(); let companion = prepare_companion(50); assert_ok!(SlowClap::propose_companion( RuntimeOrigin::signed(actor), valid_network_id, companion.clone())); assert_eq!(SlowClap::release_blocks(companion_id), 0); assert_ok!(SlowClap::release_companion( RuntimeOrigin::signed(actor), valid_network_id, companion_id)); assert_eq!(SlowClap::release_blocks(companion_id), 69 + 1); }); } #[test] fn release_companion_emits_event() { new_test_ext().execute_with(|| { let actor = alice_account_id(); let companion_id = SlowClap::current_companion_id(); let (valid_network_id, _) = prepare_networks(); let companion = prepare_companion(50); assert_ok!(SlowClap::propose_companion( RuntimeOrigin::signed(actor), valid_network_id, companion.clone())); System::reset_events(); assert_ok!(SlowClap::release_companion( RuntimeOrigin::signed(actor), valid_network_id, companion_id)); System::assert_has_event(RuntimeEvent::SlowClap( crate::Event::CompanionReleased { companion_id, network_id: valid_network_id, who: actor, release_block: 69 + 1 })); }); } #[test] fn could_not_release_from_random_account() { new_test_ext().execute_with(|| { let actor = alice_account_id(); let companion_id = SlowClap::current_companion_id(); let (valid_network_id, _) = prepare_networks(); let companion = prepare_companion(50); assert_ok!(SlowClap::propose_companion( RuntimeOrigin::signed(actor), valid_network_id, companion.clone())); assert_err!(SlowClap::release_companion( RuntimeOrigin::signed(eve_account_id()), valid_network_id, companion_id), crate::Error::::NotValidCompanion); }); } #[test] fn kill_companion_works() { new_test_ext().execute_with(|| { let actor = alice_account_id(); let companion_id = SlowClap::current_companion_id(); let (valid_network_id, _) = prepare_networks(); let companion = prepare_companion(50); assert_ok!(SlowClap::propose_companion( RuntimeOrigin::signed(actor), valid_network_id, companion.clone())); assert_ok!(SlowClap::release_companion( RuntimeOrigin::signed(actor), valid_network_id, companion_id)); let release_block = ReleaseBlocks::::get(companion_id); assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor)); assert_eq!(SlowClap::companion_details(companion_id), Some(companion)); assert_eq!(Balances::balance(&actor), 50); assert_eq!(Balances::total_issuance(), 50); System::set_block_number(release_block + 1); assert_ok!(SlowClap::kill_companion( RuntimeOrigin::signed(actor), valid_network_id, companion_id)); assert_eq!(SlowClap::companions(valid_network_id, companion_id), None); assert_eq!(SlowClap::companion_details(companion_id), None); assert_eq!(Balances::balance(&actor), 100); assert_eq!(Balances::total_issuance(), 100); }); } #[test] fn kill_companion_emits_event() { new_test_ext().execute_with(|| { let actor = alice_account_id(); let companion_id = SlowClap::current_companion_id(); let (valid_network_id, _) = prepare_networks(); let companion = prepare_companion(50); assert_ok!(SlowClap::propose_companion( RuntimeOrigin::signed(actor), valid_network_id, companion.clone())); assert_ok!(SlowClap::release_companion( RuntimeOrigin::signed(actor), valid_network_id, companion_id)); System::set_block_number( ReleaseBlocks::::get(companion_id) + 1); System::reset_events(); assert_ok!(SlowClap::kill_companion( RuntimeOrigin::signed(actor), valid_network_id, companion_id)); System::assert_has_event(RuntimeEvent::SlowClap( crate::Event::CompanionKilled { network_id: valid_network_id, who: actor, companion_id, freed_balance: 50, })); }); } #[test] fn could_not_kill_companion_that_was_not_released() { new_test_ext().execute_with(|| { let actor = alice_account_id(); let companion_id = SlowClap::current_companion_id(); let (valid_network_id, _) = prepare_networks(); let companion = prepare_companion(50); assert_ok!(SlowClap::propose_companion( RuntimeOrigin::signed(actor), valid_network_id, companion.clone())); assert_ok!(SlowClap::release_companion( RuntimeOrigin::signed(actor), valid_network_id, companion_id)); let release_block = ReleaseBlocks::::get(companion_id); assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor)); assert_eq!(SlowClap::companion_details(companion_id), Some(companion.clone())); assert_eq!(Balances::balance(&actor), 50); assert_eq!(Balances::total_issuance(), 50); System::set_block_number(release_block / 2); assert_err!(SlowClap::kill_companion( RuntimeOrigin::signed(actor), valid_network_id, companion_id), crate::Error::::ReleaseTimeHasNotCome); assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor)); assert_eq!(SlowClap::companion_details(companion_id), Some(companion)); assert_eq!(Balances::balance(&actor), 50); assert_eq!(Balances::total_issuance(), 50); }); } #[test] fn could_not_kill_companion_from_random_account() { new_test_ext().execute_with(|| { let actor = alice_account_id(); let companion_id = SlowClap::current_companion_id(); let (valid_network_id, _) = prepare_networks(); let companion = prepare_companion(50); assert_ok!(SlowClap::propose_companion( RuntimeOrigin::signed(actor), valid_network_id, companion.clone())); assert_ok!(SlowClap::release_companion( RuntimeOrigin::signed(actor), valid_network_id, companion_id)); let release_block = ReleaseBlocks::::get(companion_id); assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor)); assert_eq!(SlowClap::companion_details(companion_id), Some(companion.clone())); assert_eq!(Balances::balance(&actor), 50); assert_eq!(Balances::total_issuance(), 50); System::set_block_number(release_block + 1); assert_err!(SlowClap::kill_companion( RuntimeOrigin::signed(eve_account_id()), valid_network_id, companion_id), crate::Error::::NotValidCompanion); assert_eq!(SlowClap::companions(valid_network_id, companion_id), Some(actor)); assert_eq!(SlowClap::companion_details(companion_id), Some(companion)); assert_eq!(Balances::balance(&actor), 50); assert_eq!(Balances::total_issuance(), 50); }); } #[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 request_body = SlowClap::prepare_request_body( None, 0, Vec::new(), Vec::new()); 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 request_body = SlowClap::prepare_request_body( Some(1337), 69, get_gatekeeper(), get_topic_name()); assert_eq!( core::str::from_utf8(&request_body).unwrap(), r#"{"id":0,"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":1268,"toBlock":1337,"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 request_body = SlowClap::prepare_request_body( None, 0, Vec::new(), Vec::new()); let raw_response = SlowClap::fetch_from_remote( get_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 request_body = SlowClap::prepare_request_body( Some(20335857), 84, get_gatekeeper(), get_topic_name()); let raw_response = SlowClap::fetch_from_remote( get_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 evm_response = SlowClap::fetch_and_parse( None, 0, Vec::::new(), Vec::::new(), get_rpc_endpoint()).unwrap(); let evm_block_number = match evm_response { EvmResponseType::BlockNumber(evm_block) => Some(evm_block), _ => None, }; assert_eq!(evm_block_number.unwrap_or_default(), 20335745); }); } #[test] fn should_make_http_call_and_parse_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 expected_logs = get_expected_logs(); t.execute_with(|| { let evm_response = SlowClap::fetch_and_parse( Some(20335857), 84, get_gatekeeper(), get_topic_name(), get_rpc_endpoint() ).unwrap(); let evm_logs = match evm_response { EvmResponseType::TransactionLogs(logs) => Some(logs), _ => None, }; assert!(evm_logs.is_some()); let evm_logs = evm_logs.unwrap(); assert_eq!(evm_logs, expected_logs); }); } #[test] fn should_catch_error_in_json_response() { let (offchain, state) = TestOffchainExt::new(); let mut t = sp_io::TestExternalities::default(); t.register_extension(OffchainWorkerExt::new(offchain)); evm_error_response(&mut state.write()); t.execute_with(|| { assert_err!(SlowClap::fetch_and_parse( None, 0, Vec::::new(), Vec::::new(), get_rpc_endpoint()), OffchainErr::ErrorInEvmResponse); }); } // #[test] // fn conversion_to_claps_is_correct() { // todo!(); // } // // #[test] // fn evm_block_storage_is_empty_by_default() { // todo!(); // } // // #[test] // fn evm_block_is_stored_locally_after_first_response() { // todo!(); // } // // #[test] // fn evm_block_storage_is_none_after_logs() { // todo!(); // } // // #[test] // fn send_evm_usigned_transaction_from_authority() { // todo!(); // } // #[test] // fn should_report_offline_validators() { // new_test_ext().execute_with(|| { // let block = 1; // System::set_block_number(block); // advance_session(); // let validators = vec![1, 2, 3, 4, 5, 6]; // Validators::mutate(|l| *l = Some(validators.clone())); // advance_session(); // // advance_session(); // // let offences = Offences::take(); // assert_eq!( // offences, // vec![( // vec![], // ThrottlingOffence { // session_index: 2, // validator_set_count: 3, // offenders: vec![(1, 1), (2, 2), (3, 3)], // } // )] // ); // // for (idx, v) in validators.into_iter().take(4).enumerate() { // let _ = clap(); // } // // advance_session(); // // let offences = Offences::take(); // assert_eq!( // offences, // vec![( // vec![], // ThrottlingOffence { // session_index: 3, // validator_set_count: 6, // offenders: vec![(5, 5), (6, 6)], // } // )] // ); // }); // } // // #[test] // fn should_increase_actions_of_validators_when_clap_is_received() { // } // // #[test] // fn same_claps_should_not_increase_actions() { // } // // #[test] // fn should_cleanup_received_claps_on_session_end() { // } // // #[test] // fn should_mark_validator_if_disabled() { // } fn get_rpc_endpoint() -> Vec { b"https://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/".to_vec() } fn get_gatekeeper() -> Vec { b"0x4d224452801ACEd8B2F0aebE155379bb5D594381".to_vec() } fn get_topic_name() -> Vec { b"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef".to_vec() } fn get_network_id() -> u32 { // let mut network_id: NetworkIdOf = Default::default(); // network_id.saturating_inc(); // network_id 1u32 } 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://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/".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_error_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://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/".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\",\"error\":\"some bad error occures :-(\"}".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":20335773,"toBlock":20335857,"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://nd-422-757-666.p2pify.com/0a9d79d93fb2f4a4b1e04695da2b77a7/".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() }); } fn get_expected_logs() -> Vec { let byte_converter = |s: &str| -> Vec { (2..s.len()) .step_by(2) .map(|i| u8::from_str_radix(&s[i..i+2], 16).expect("valid u8 symbol; qed")) .collect() }; vec![ Log { transaction_hash: Some(H256::from_slice(&byte_converter("0xa82f2fe87f4ba01ab3bd2cd4d0fe75a26f0e9a37e2badc004a0e38f9d088a758"))), block_number: Some(20335773), topics: vec![ byte_converter("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), byte_converter("0x000000000000000000000000d91efec7e42f80156d1d9f660a69847188950747"), byte_converter("0x0000000000000000000000005e611dfbe71b988cf2a3e5fe4191b5e3d4c4212a"), ], address: Some(b"0x4d224452801aced8b2f0aebe155379bb5d594381".to_vec()), data: sp_std::collections::btree_map::BTreeMap::from([(0, 1308625900000000000000)]), removed: false, }, Log { transaction_hash: Some(H256::from_slice(&byte_converter("0xd9f02b79a90db7536b0afb5e24bbc1f4319dc3d8c57af7c39941acd249ec053a"))), block_number: Some(20335857), topics: vec![ byte_converter("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), byte_converter("0x0000000000000000000000005954ab967bc958940b7eb73ee84797dc8a2afbb9"), byte_converter("0x000000000000000000000000465165be7cacdbd2cbc8334f549fab9783ad6e7a"), ], address: Some(b"0x4d224452801aced8b2f0aebe155379bb5d594381".to_vec()), data: sp_std::collections::btree_map::BTreeMap::from([(0, 45862703604421729909)]), removed: false, }, ] }