tests added for new offchain worker functionality
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
This commit is contained in:
parent
b282ebad62
commit
eb9fc16b43
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3837,7 +3837,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ghost-slow-clap"
|
||||
version = "0.4.11"
|
||||
version = "0.4.12"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ghost-slow-clap"
|
||||
version = "0.4.11"
|
||||
version = "0.4.12"
|
||||
description = "Applause protocol for the EVM bridge"
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
@ -881,10 +881,10 @@ impl<T: Config> Pallet<T> {
|
||||
Self::prepare_evm_request_body_for_latest_block(network_data)
|
||||
};
|
||||
|
||||
let pending_requests_metadata =
|
||||
let pending_requests =
|
||||
Self::prepare_pending_evm_requests(&rpc_endpoints, &request_body)?;
|
||||
let parsed_evm_responses =
|
||||
Self::fetch_multiple_evm_from_remote(pending_requests_metadata)
|
||||
Self::fetch_multiple_evm_from_remote(pending_requests)
|
||||
.iter()
|
||||
.filter_map(|response_bytes| {
|
||||
let parsed_evm_response =
|
||||
@ -965,8 +965,8 @@ impl<T: Config> Pallet<T> {
|
||||
fn prepare_pending_evm_requests(
|
||||
rpc_endpoints: &Vec<Vec<u8>>,
|
||||
request_body: &[u8],
|
||||
) -> OffchainResult<T, Vec<(PendingRequest, String)>> {
|
||||
let mut pending_requests_metadata = Vec::new();
|
||||
) -> OffchainResult<T, Vec<PendingRequest>> {
|
||||
let mut pending_requests = Vec::new();
|
||||
let request_body_str = core::str::from_utf8(request_body)
|
||||
.map_err(|_| OffchainErr::UnparsableRequestBody(request_body.to_vec()))?;
|
||||
|
||||
@ -977,7 +977,11 @@ impl<T: Config> Pallet<T> {
|
||||
let rpc_endpoint_str = match core::str::from_utf8(rpc_endpoint) {
|
||||
Ok(rpc_endpoint_str) => rpc_endpoint_str,
|
||||
Err(_) => {
|
||||
log::info!(target: LOG_TARGET, "👻 Could not get valid utf8 rpc endpoint from bytes: {:?}", rpc_endpoint);
|
||||
log::info!(
|
||||
target: LOG_TARGET,
|
||||
"👻 Could not get valid utf8 rpc endpoint from bytes; skipped \"{:?}\"",
|
||||
rpc_endpoint,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
@ -989,21 +993,30 @@ impl<T: Config> Pallet<T> {
|
||||
.send()
|
||||
{
|
||||
Ok(pending) => {
|
||||
pending_requests_metadata.push((pending, rpc_endpoint_str.to_string()))
|
||||
pending_requests.push(pending)
|
||||
}
|
||||
Err(_) => {
|
||||
log::info!(target: LOG_TARGET, "👻 Request skipped: failed to send request to \"{}\"", rpc_endpoint_str)
|
||||
log::info!(
|
||||
target: LOG_TARGET,
|
||||
"👻 Request skipped: failed to send request to \"{}\"",
|
||||
rpc_endpoint_str,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pending_requests_metadata.len() == 0 {
|
||||
if pending_requests.len() == 0 {
|
||||
return Err(OffchainErr::NoRequestsSent);
|
||||
}
|
||||
|
||||
log::info!(target: LOG_TARGET, "👻 Requests sent {} out of {}", pending_requests_metadata.len(), rpc_endpoints.len());
|
||||
log::info!(
|
||||
target: LOG_TARGET,
|
||||
"👻 Requests sent {} out of {}",
|
||||
pending_requests.len(),
|
||||
rpc_endpoints.len(),
|
||||
);
|
||||
|
||||
Ok(pending_requests_metadata)
|
||||
Ok(pending_requests)
|
||||
}
|
||||
|
||||
fn get_balanced_evm_response(
|
||||
@ -1067,31 +1080,24 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
|
||||
fn fetch_multiple_evm_from_remote(
|
||||
pending_requests_metadata: Vec<(PendingRequest, String)>,
|
||||
pending_requests: Vec<PendingRequest>,
|
||||
) -> Vec<Vec<u8>> {
|
||||
let pending_requests = pending_requests_metadata
|
||||
.iter()
|
||||
.map(|(pending_request, _)| PendingRequest {
|
||||
id: pending_request.id,
|
||||
})
|
||||
.collect::<Vec<PendingRequest>>();
|
||||
let mut requests_failed = 0;
|
||||
let mut responses_failed = 0;
|
||||
let mut responses_non_200 = 0;
|
||||
|
||||
let pending_requests_len = pending_requests.len();
|
||||
let deadline = sp_io::offchain::timestamp()
|
||||
.add(rt_offchain::Duration::from_millis(FETCH_TIMEOUT_PERIOD));
|
||||
|
||||
PendingRequest::try_wait_all(pending_requests, Some(deadline))
|
||||
let parsed_evm_responses = PendingRequest::try_wait_all(pending_requests, Some(deadline))
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, pending_request)| {
|
||||
let url = pending_requests_metadata.get(index)
|
||||
.map(|(_, url)| url.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
.filter_map(|pending_request| {
|
||||
// handle request-level errors (transport/connection failures)
|
||||
let request_result = match pending_request {
|
||||
Ok(request) => request,
|
||||
Err(err) => {
|
||||
log::info!(target: LOG_TARGET, "👻 Request skipped; request to \"{}\" failed: {:?}", url, err);
|
||||
Err(_) => {
|
||||
requests_failed += 1;
|
||||
return None;
|
||||
}
|
||||
};
|
||||
@ -1099,20 +1105,32 @@ impl<T: Config> Pallet<T> {
|
||||
// handle response-level errors (HTTP/protocol errors)
|
||||
let response = match request_result {
|
||||
Ok(response) => response,
|
||||
Err(err) => {
|
||||
log::info!(target: LOG_TARGET, "👻 Response skipped from \"{}\" error: {:?}", url, err);
|
||||
Err(_) => {
|
||||
responses_failed += 1;
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if response.code != 200 {
|
||||
log::info!(target: LOG_TARGET, "👻 Response skipped from \"{}\": status {}", url, response.code);
|
||||
responses_non_200 += 1;
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(response.body().collect::<Vec<u8>>())
|
||||
})
|
||||
.collect()
|
||||
.collect::<Vec<Vec<u8>>>();
|
||||
|
||||
log::info!(
|
||||
target: LOG_TARGET,
|
||||
"👻 Fetched {} of {}: {} failed request, {} failed responses, {} non 200 code",
|
||||
pending_requests_len,
|
||||
parsed_evm_responses.len(),
|
||||
requests_failed,
|
||||
responses_failed,
|
||||
responses_non_200,
|
||||
);
|
||||
|
||||
parsed_evm_responses
|
||||
}
|
||||
|
||||
fn prepare_evm_request_body_for_latest_block(network_data: &NetworkData) -> Vec<u8> {
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use super::*;
|
||||
use crate::evm_types::Log;
|
||||
use crate::mock::*;
|
||||
|
||||
use frame_support::{assert_err, assert_ok, dispatch};
|
||||
@ -293,6 +294,162 @@ fn should_make_http_call_and_parse_logs() {
|
||||
});
|
||||
}
|
||||
|
||||
#[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);
|
||||
@ -1363,11 +1520,135 @@ fn should_split_commit_slash_between_active_validators() {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: add tests
|
||||
// 1. prepare_pending_evm_requests fails fully and partially
|
||||
// 2. fetch_multiple_evm_from_remote when they give different results
|
||||
// 3. check_evm_responses_correctness
|
||||
// 4. get_balanced_evm_response
|
||||
#[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 empty_responses = vec![];
|
||||
assert_err!(
|
||||
SlowClap::get_balanced_evm_response(&empty_responses),
|
||||
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)
|
||||
.expect("median block should be extractable; qed");
|
||||
assert_eq!(median_block, EvmResponseType::BlockNumber(421));
|
||||
|
||||
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)
|
||||
.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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user