1954 lines
67 KiB
Rust
1954 lines
67 KiB
Rust
#![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<u32>> = 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<u32>> = 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<u32>> = 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<u32>> = 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<u32>> = 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<u32>> = 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<u32>> = 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<u32>> = 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<u32>> = 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<u32>> = 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::<Vec<EvmResponseType>>();
|
|
|
|
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::<Runtime>::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::<Runtime>::get(&session_index, &unique_hash).is_some(),
|
|
true
|
|
);
|
|
|
|
for _ in 0..history_depth {
|
|
advance_session();
|
|
}
|
|
|
|
assert_eq!(
|
|
ApplauseDetails::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::DisabledAuthority,
|
|
);
|
|
assert_eq!(
|
|
ApplauseDetails::<Runtime>::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::<Runtime>::DisabledAuthority,
|
|
);
|
|
assert_eq!(
|
|
ApplauseDetails::<Runtime>::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::<RewardCurve, Runtime>::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::<Runtime>::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::<Runtime>::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::<Vec<_>>();
|
|
|
|
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::<Runtime>::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::<Runtime>::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::<Runtime>::TimeWentBackwards,
|
|
);
|
|
}
|
|
|
|
let block_commitments = BlockCommitments::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::get(network_id), 0);
|
|
assert_eq!(BlockCommitments::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::TimeWentBackwards,
|
|
);
|
|
|
|
BlockCommitments::<Runtime>::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::<Runtime>::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::<Runtime>::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::<Runtime>::get(&session_index),
|
|
Some(disabled_bitmp.clone())
|
|
);
|
|
Session::disable_index(3);
|
|
disabled_bitmp.set(3);
|
|
assert_eq!(
|
|
DisabledAuthorityIndexes::<Runtime>::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::<Runtime>::get(network_id);
|
|
assert_eq!(
|
|
DisabledAuthorityIndexes::<Runtime>::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::<Runtime>::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::<Runtime>::TimeWentBackwards
|
|
);
|
|
assert_err!(
|
|
do_block_commitment(session_index, network_id, 3, very_old_block),
|
|
Error::<Runtime>::TimeWentBackwards
|
|
);
|
|
|
|
BlockCommitments::<Runtime>::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::<Runtime>::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::<Runtime>::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<Runtime>,
|
|
) {
|
|
let maybe_details = ApplauseDetails::<Runtime>::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::<Runtime>::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<u32>,
|
|
maybe_incoming_commission: Option<u32>,
|
|
) -> 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::<Runtime>::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::<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>,
|
|
maybe_block_number: Option<u64>,
|
|
) -> (NetworkIdOf<Runtime>, 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<u8> {
|
|
b"https://rpc.endpoint.network.com".to_vec()
|
|
}
|
|
|
|
fn get_rpc_endpoints() -> Vec<Vec<u8>> {
|
|
vec![
|
|
get_rpc_endpoint(),
|
|
b"https://other.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, 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()
|
|
});
|
|
}
|