forked from ghostchain/ghost-node
860 lines
34 KiB
Rust
860 lines
34 KiB
Rust
#![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<u32>,
|
|
maybe_incoming_commission: Option<u32>,
|
|
) -> 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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::ClapForWrongSession);
|
|
assert_err!(do_clap_from(session_index_down, network_id, 0, false),
|
|
Error::<Runtime>::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::<Runtime>::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::<Runtime>::CurrentValidatorIsDisabled);
|
|
|
|
assert_eq!(pallet::ReceivedClaps::<Runtime>::get(&storage_key).len(), 0);
|
|
assert_eq!(pallet::ClapsInSession::<Runtime>::get(&session_index).len(), 1);
|
|
assert_eq!(pallet::ClapsInSession::<Runtime>::get(&session_index)
|
|
.into_iter()
|
|
.filter(|(_, v)| !v.disabled)
|
|
.collect::<Vec<_>>()
|
|
.len(), 0);
|
|
|
|
assert_ok!(do_clap_from(session_index, network_id, 1, false));
|
|
assert_eq!(Session::disable_index(1), true);
|
|
|
|
assert_eq!(pallet::ReceivedClaps::<Runtime>::get(&storage_key).len(), 1);
|
|
assert_eq!(pallet::ClapsInSession::<Runtime>::get(&session_index).len(), 2);
|
|
assert_eq!(pallet::ClapsInSession::<Runtime>::get(&session_index)
|
|
.into_iter()
|
|
.filter(|(_, v)| !v.disabled)
|
|
.collect::<Vec<_>>()
|
|
.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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<RewardCurve, Runtime>::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::<RewardCurve, Runtime>::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::<Runtime>::NotEnoughClaps);
|
|
|
|
assert_eq!(pallet::ApplausesForTransaction::<Runtime>::get(&storage_key), false);
|
|
assert_eq!(Balances::balance(&receiver), 0);
|
|
assert_eq!(BridgedInflationCurve::<RewardCurve, Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Vec<_>>();
|
|
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::<Runtime>::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::<u64>::new());
|
|
advance_session();
|
|
|
|
Session::session_index()
|
|
}
|
|
|
|
fn generate_unique_hash(
|
|
maybe_network_id: Option<u32>,
|
|
maybe_transaction_hash: Option<H256>,
|
|
maybe_receiver: Option<u64>,
|
|
maybe_amount: Option<u64>,
|
|
) -> (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::<Runtime>::get(storage_key).len(), index);
|
|
assert_eq!(pallet::ClapsInSession::<Runtime>::get(session_index).len(), index);
|
|
}
|
|
|
|
fn get_rpc_endpoint() -> Vec<u8> {
|
|
b"https://rpc.endpoint.network.com".to_vec()
|
|
}
|
|
|
|
fn get_gatekeeper() -> Vec<u8> {
|
|
b"0x4d224452801ACEd8B2F0aebE155379bb5D594381".to_vec()
|
|
}
|
|
|
|
fn get_topic_name() -> Vec<u8> {
|
|
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()
|
|
});
|
|
}
|