Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove a 'dev-dependency' of 'near-client' on 'testlib' to prevent circular dependencies. #4606

Merged
merged 28 commits into from
Aug 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f0224db
Remove dependency of 'chain/client' on 'nearcore' by moving its tests…
nikurt Jul 26, 2021
49d328f
Remove dependency of 'chain/client' on 'nearcore' by moving its tests…
nikurt Jul 26, 2021
9e29ada
Remove dependency of 'chain/client' on 'nearcore' by moving its tests…
nikurt Jul 26, 2021
b5da0e7
rustfmt
nikurt Jul 26, 2021
f3d49f4
better layout of near-client integration tests
nikurt Jul 26, 2021
6166ca5
better layout of near-client integration tests
nikurt Jul 26, 2021
5073865
.
nikurt Jul 27, 2021
aa081b7
Merge branch 'master' into cycle-t4
nikurt Jul 28, 2021
4e6ecac
.
nikurt Jul 28, 2021
49eb3a5
update nightly features of integration tests
nikurt Jul 28, 2021
8d0f758
merged
nikurt Jul 28, 2021
1ae217a
Merged
nikurt Jul 29, 2021
110b417
metric_recorder feature
nikurt Jul 29, 2021
15e848b
Merge branch 'master' of https://github.com/near/nearcore into cycle-n
nikurt Jul 30, 2021
d923af6
Remove a 'dev-dependency' of 'near-client' on 'testlib' to prevent ci…
nikurt Jul 30, 2021
df0b1b4
Merge branch 'master' into cycle-n
nikurt Aug 2, 2021
0d2e2fa
Remove dependency of 'chain/client' on 'nearcore' by moving its tests…
nikurt Jul 26, 2021
4b8cd31
Remove dependency of 'chain/client' on 'nearcore' by moving its tests…
nikurt Jul 26, 2021
6179cba
Remove dependency of 'chain/client' on 'nearcore' by moving its tests…
nikurt Jul 26, 2021
2583ec3
merge
nikurt Aug 2, 2021
f31fafb
merge
nikurt Aug 2, 2021
e015d10
merge
nikurt Aug 2, 2021
a71fa25
merge
nikurt Aug 2, 2021
00b90b0
merge
nikurt Aug 2, 2021
5be00c2
feature protocol_feature_block_header_v3 is required to run the new i…
nikurt Aug 2, 2021
a71c27b
Merge branch 'master' into cycle-n
bowenwang1996 Aug 2, 2021
76a7f68
Merge refs/heads/master into cycle-n
near-bulldozer[bot] Aug 2, 2021
3374c5c
Merge refs/heads/master into cycle-n
near-bulldozer[bot] Aug 2, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion chain/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ delay-detector = { path = "../../tools/delay_detector", optional = true }
[dev-dependencies]
near-logger-utils = { path = "../../test-utils/logger" }
near-actix-test-utils = { path = "../../test-utils/actix-test-utils" }
testlib = { path = "../../test-utils/testlib" }
near-test-contracts = { path = "../../runtime/near-test-contracts" }

[features]
Expand Down
279 changes: 4 additions & 275 deletions chain/client/tests/chunks_management.rs
Original file line number Diff line number Diff line change
@@ -1,290 +1,19 @@
use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, RwLock};
use std::time::Instant;
use std::collections::HashSet;

use actix::{Addr, System};
use futures::{future, FutureExt};
use log::info;

use near_actix_test_utils::run_actix;
use near_chain::ChainGenesis;
use near_chunks::{
CHUNK_REQUEST_RETRY_MS, CHUNK_REQUEST_SWITCH_TO_FULL_FETCH_MS,
CHUNK_REQUEST_SWITCH_TO_OTHERS_MS,
};
use near_client::test_utils::{setup_mock_all_validators, TestEnv};
use near_client::{ClientActor, GetBlock, ViewClientActor};
use near_client::test_utils::TestEnv;
use near_crypto::KeyType;
use near_logger_utils::{init_integration_logger, init_test_logger};
use near_network::types::{AccountIdOrPeerTrackingShard, PartialEncodedChunkRequestMsg};
use near_network::{NetworkClientMessages, NetworkRequests, NetworkResponses, PeerInfo};
use near_network::types::PartialEncodedChunkRequestMsg;
use near_network::NetworkRequests;
use near_primitives::hash::{hash, CryptoHash};
#[cfg(feature = "protocol_feature_block_header_v3")]
use near_primitives::sharding::ShardChunkHeaderInner;
use near_primitives::sharding::{
ChunkHash, PartialEncodedChunkV2, ShardChunkHeader, ShardChunkHeaderV2,
};
use near_primitives::transaction::SignedTransaction;
use near_primitives::types::BlockHeight;
use near_primitives::validator_signer::InMemoryValidatorSigner;
use testlib::test_helpers::heavy_test;

#[test]
fn chunks_produced_and_distributed_all_in_all_shards() {
heavy_test(|| {
run_actix(async {
chunks_produced_and_distributed_common(1, false, 15 * CHUNK_REQUEST_RETRY_MS);
});
});
}

#[test]
fn chunks_produced_and_distributed_2_vals_per_shard() {
heavy_test(|| {
run_actix(async {
chunks_produced_and_distributed_common(2, false, 15 * CHUNK_REQUEST_RETRY_MS);
});
});
}

#[test]
fn chunks_produced_and_distributed_one_val_per_shard() {
heavy_test(|| {
run_actix(async {
chunks_produced_and_distributed_common(4, false, 15 * CHUNK_REQUEST_RETRY_MS);
});
});
}

/// The timeout for requesting chunk from others is 1s. 3000 block timeout means that a participant
/// that is otherwise ready to produce a block will wait for 3000/2 milliseconds for all the chunks.
/// We block all the communication from test1 to test4, and expect that in 1.5 seconds test4 will
/// give up on getting the part from test1 and will get it from test2 (who will have it because
/// `validator_groups=2`)
#[test]
fn chunks_recovered_from_others() {
heavy_test(|| {
run_actix(async {
chunks_produced_and_distributed_common(2, true, 4 * CHUNK_REQUEST_SWITCH_TO_OTHERS_MS);
});
});
}

/// Same test as above, but the number of validator groups is four, therefore test2 doesn't have the
/// part test4 needs. The only way test4 can recover the part is by reconstructing the whole chunk,
/// but they won't do it for the first 3 seconds, and 3s block_timeout means that the block producers
/// only wait for 3000/2 milliseconds until they produce a block with some chunks missing
#[test]
#[should_panic]
fn chunks_recovered_from_full_timeout_too_short() {
heavy_test(|| {
run_actix(async {
chunks_produced_and_distributed_common(4, true, 2 * CHUNK_REQUEST_SWITCH_TO_OTHERS_MS);
});
});
}

/// Same test as above, but the timeout is sufficiently large for test4 now to reconstruct the full
/// chunk
#[test]
fn chunks_recovered_from_full() {
heavy_test(|| {
run_actix(async {
chunks_produced_and_distributed_common(
4,
true,
2 * CHUNK_REQUEST_SWITCH_TO_FULL_FETCH_MS,
);
})
});
}

/// Runs block producing client and stops after network mock received seven blocks
/// Confirms that the blocks form a chain (which implies the chunks are distributed).
/// Confirms that the number of messages transmitting the chunks matches the expected number.
fn chunks_produced_and_distributed_common(
validator_groups: u64,
drop_from_1_to_4: bool,
block_timeout: u64,
) {
init_test_logger();

let connectors: Arc<RwLock<Vec<(Addr<ClientActor>, Addr<ViewClientActor>)>>> =
Arc::new(RwLock::new(vec![]));
let heights = Arc::new(RwLock::new(HashMap::new()));
let heights1 = heights.clone();

let height_to_hash = Arc::new(RwLock::new(HashMap::new()));
let height_to_epoch = Arc::new(RwLock::new(HashMap::new()));

let check_height =
move |hash: CryptoHash, height| match heights1.write().unwrap().entry(hash.clone()) {
Entry::Occupied(entry) => {
assert_eq!(*entry.get(), height);
}
Entry::Vacant(entry) => {
entry.insert(height);
}
};

let validators =
vec![vec!["test1", "test2", "test3", "test4"], vec!["test5", "test6", "test7", "test8"]];
let key_pairs = (0..8).map(|_| PeerInfo::random()).collect::<Vec<_>>();

let mut partial_chunk_msgs = 0;
let mut partial_chunk_request_msgs = 0;

let (_, conn, _) = setup_mock_all_validators(
validators.clone(),
key_pairs.clone(),
validator_groups,
true,
block_timeout,
false,
false,
5,
true,
vec![false; validators.iter().map(|x| x.len()).sum()],
vec![true; validators.iter().map(|x| x.len()).sum()],
false,
Arc::new(RwLock::new(Box::new(move |from_whom: String, msg: &NetworkRequests| {
match msg {
NetworkRequests::Block { block } => {
check_height(*block.hash(), block.header().height());
check_height(*block.header().prev_hash(), block.header().height() - 1);

let h = block.header().height();

let mut height_to_hash = height_to_hash.write().unwrap();
height_to_hash.insert(h, *block.hash());

let mut height_to_epoch = height_to_epoch.write().unwrap();
height_to_epoch.insert(h, block.header().epoch_id().clone());

println!(
"[{:?}]: BLOCK {} HEIGHT {}; HEADER HEIGHTS: {} / {} / {} / {};\nAPPROVALS: {:?}",
Instant::now(),
block.hash(),
block.header().height(),
block.chunks()[0].height_created(),
block.chunks()[1].height_created(),
block.chunks()[2].height_created(),
block.chunks()[3].height_created(),
block.header().approvals(),
);

if h > 1 {
// Make sure doomslug finality is computed correctly.
assert_eq!(
block.header().last_ds_final_block(),
height_to_hash.get(&(h - 1)).unwrap()
);

// Make sure epoch length actually corresponds to the desired epoch length
// The switches are expected at 0->1, 5->6 and 10->11
let prev_epoch_id = height_to_epoch.get(&(h - 1)).unwrap().clone();
assert_eq!(block.header().epoch_id() == &prev_epoch_id, h % 5 != 1);

// Make sure that the blocks leading to the epoch switch have twice as
// many approval slots
assert_eq!(block.header().approvals().len() == 8, h % 5 == 0 || h % 5 == 4);
}
if h > 2 {
// Make sure BFT finality is computed correctly
assert_eq!(
block.header().last_final_block(),
height_to_hash.get(&(h - 2)).unwrap()
);
}

if block.header().height() > 1 {
for shard_id in 0..4 {
// If messages from 1 to 4 are dropped, 4 at their heights will
// receive the block significantly later than the chunks, and
// thus would discard the chunks
if !drop_from_1_to_4 || block.header().height() % 4 != 3 {
assert_eq!(
block.header().height(),
block.chunks()[shard_id].height_created()
);
}
}
}

if block.header().height() >= 12 {
println!("PREV BLOCK HASH: {}", block.header().prev_hash());
println!(
"STATS: responses: {} requests: {}",
partial_chunk_msgs, partial_chunk_request_msgs
);

System::current().stop();
}
}
NetworkRequests::PartialEncodedChunkMessage {
account_id: to_whom,
partial_encoded_chunk: _,
} => {
partial_chunk_msgs += 1;
if drop_from_1_to_4 && from_whom == "test1" && to_whom == "test4" {
println!("Dropping Partial Encoded Chunk Message from test1 to test4");
return (NetworkResponses::NoResponse, false);
}
}
NetworkRequests::PartialEncodedChunkForward { account_id: to_whom, .. } => {
if drop_from_1_to_4 && from_whom == "test1" && to_whom == "test4" {
println!(
"Dropping Partial Encoded Chunk Forward Message from test1 to test4"
);
return (NetworkResponses::NoResponse, false);
}
}
NetworkRequests::PartialEncodedChunkResponse { route_back: _, response: _ } => {
partial_chunk_msgs += 1;
}
NetworkRequests::PartialEncodedChunkRequest {
target: AccountIdOrPeerTrackingShard { account_id: Some(to_whom), .. },
request: _,
} => {
if drop_from_1_to_4 && from_whom == "test4" && to_whom == "test1" {
info!("Dropping Partial Encoded Chunk Request from test4 to test1");
return (NetworkResponses::NoResponse, false);
}
if drop_from_1_to_4 && from_whom == "test4" && to_whom == "test2" {
info!("Observed Partial Encoded Chunk Request from test4 to test2");
}
partial_chunk_request_msgs += 1;
}
_ => {}
};
(NetworkResponses::NoResponse, true)
}))),
);
*connectors.write().unwrap() = conn;

let view_client = connectors.write().unwrap()[0].1.clone();
actix::spawn(view_client.send(GetBlock::latest()).then(move |res| {
let block_hash = res.unwrap().unwrap().header.hash;
let connectors_ = connectors.write().unwrap();
connectors_[0].0.do_send(NetworkClientMessages::Transaction {
transaction: SignedTransaction::empty(block_hash),
is_forwarded: false,
check_only: false,
});
connectors_[1].0.do_send(NetworkClientMessages::Transaction {
transaction: SignedTransaction::empty(block_hash),
is_forwarded: false,
check_only: false,
});
connectors_[2].0.do_send(NetworkClientMessages::Transaction {
transaction: SignedTransaction::empty(block_hash),
is_forwarded: false,
check_only: false,
});
future::ready(())
}));
}

#[test]
fn test_request_chunk_restart() {
Expand Down
1 change: 1 addition & 0 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ chrono = { version = "0.4.4", features = ["serde"] }
funty = "=1.1.0" # Pin dependency to avoid compilation errors: https://github.com/myrrlyn/funty/issues/3
futures = "0.3"
hex = "0.4"
log = "0.4"
rand = "0.7"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Expand Down
Loading