{
self.chain.flush();
self.queue.write().pop_front()
}
fn is_done(&self) -> bool {
self.queue.read().is_empty() && self.is_io_queue_empty() && self.is_new_blocks_queue_empty()
}
fn sync_step(&self) {
let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None, self.private_state_db());
self.chain.flush();
self.sync.write().maintain_peers(&mut io);
self.sync.write().maintain_sync(&mut io);
self.sync.write().continue_sync(&mut io);
self.sync.write().propagate_new_transactions(&mut io);
}
fn restart_sync(&self) {
self.sync.write().restart(&mut TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None, self.private_state_db()));
}
fn process_all_io_messages(&self) {
if !self.is_io_queue_empty() {
while let Some(message) = self.io_queue.write().pop_front() {
self.process_io_message(message);
}
}
}
fn process_all_new_block_messages(&self) {
if !self.is_new_blocks_queue_empty() {
while let Some(message) = self.new_blocks_queue.write().pop_front() {
self.process_new_block_message(message);
}
}
}
}
pub struct TestNet {
pub peers: Vec>,
pub started: bool,
pub disconnect_events: Vec<(PeerId, PeerId)>, //disconnected (initiated by, to)
}
impl TestNet> {
pub fn new(n: usize) -> Self {
Self::new_with_config(n, SyncConfig::default())
}
pub fn new_with_fork(n: usize, fork: Option<(BlockNumber, H256)>) -> Self {
let mut config = SyncConfig::default();
config.fork_block = fork;
Self::new_with_config(n, config)
}
pub fn new_with_config(n: usize, config: SyncConfig) -> Self {
let mut net = TestNet {
peers: Vec::new(),
started: false,
disconnect_events: Vec::new(),
};
for _ in 0..n {
let chain = TestBlockChainClient::new();
let ss = Arc::new(TestSnapshotService::new());
let private_tx_handler = Arc::new(SimplePrivateTxHandler::default());
let sync = ChainSync::new(config.clone(), &chain, Some(private_tx_handler.clone()));
net.peers.push(Arc::new(EthPeer {
sync: RwLock::new(sync),
snapshot_service: ss,
chain: Arc::new(chain),
miner: Arc::new(Miner::new_for_tests(&spec::new_test(), None)),
queue: RwLock::new(VecDeque::new()),
private_tx_handler,
io_queue: RwLock::new(VecDeque::new()),
new_blocks_queue: RwLock::new(VecDeque::new()),
private_state_db: RwLock::new(None),
}));
}
net
}
// relies on Arc uniqueness, which is only true when we haven't registered a ChainNotify.
pub fn peer_mut(&mut self, i: usize) -> &mut EthPeer {
Arc::get_mut(&mut self.peers[i]).expect("Arc never exposed externally")
}
}
impl TestNet> {
pub fn with_spec(
n: usize,
config: SyncConfig,
spec_factory: F,
) -> Self
where F: Fn() -> Spec
{
let mut net = TestNet {
peers: Vec::new(),
started: false,
disconnect_events: Vec::new(),
};
for _ in 0..n {
net.add_peer_with_private_config(config.clone(), spec_factory());
}
net
}
pub fn add_peer_with_private_config(&mut self, config: SyncConfig, spec: Spec) {
let channel = IoChannel::disconnected();
let miner = Arc::new(Miner::new_for_tests(&spec, None));
let client = EthcoreClient::new(
ClientConfig::default(),
&spec,
test_helpers::new_db(),
miner.clone(),
channel.clone()
).unwrap();
let private_tx_handler = Arc::new(SimplePrivateTxHandler::default());
let ss = Arc::new(TestSnapshotService::new());
let sync = ChainSync::new(config, &*client, Some(private_tx_handler.clone()));
let peer = Arc::new(EthPeer {
sync: RwLock::new(sync),
snapshot_service: ss,
chain: client,
miner,
queue: RwLock::new(VecDeque::new()),
private_tx_handler,
io_queue: RwLock::new(VecDeque::new()),
new_blocks_queue: RwLock::new(VecDeque::new()),
private_state_db: RwLock::new(None),
});
peer.chain.add_notify(peer.clone());
//private_provider.add_notify(peer.clone());
self.peers.push(peer);
}
}
impl TestNet
where P: Peer {
pub fn peer(&self, i: usize) -> &P {
&self.peers[i]
}
pub fn start(&mut self) {
if self.started {
return;
}
for peer in 0..self.peers.len() {
for client in 0..self.peers.len() {
if peer != client {
self.peers[peer].on_connect(client as PeerId);
}
}
}
self.started = true;
}
pub fn sync_step(&mut self) {
for peer in 0..self.peers.len() {
let packet = self.peers[peer].pending_message();
if let Some(packet) = packet {
let disconnecting = {
let recipient = packet.recipient();
trace!("--- {} -> {} ---", peer, recipient);
let to_disconnect = self.peers[recipient].receive_message(peer as PeerId, packet);
for d in &to_disconnect {
// notify this that disconnecting peers are disconnecting
self.peers[recipient].on_disconnect(*d as PeerId);
self.disconnect_events.push((peer, *d));
}
to_disconnect
};
for d in &disconnecting {
// notify other peers that this peer is disconnecting
self.peers[*d].on_disconnect(peer as PeerId);
}
}
self.sync_step_peer(peer);
}
}
pub fn sync_step_peer(&mut self, peer_num: usize) {
self.peers[peer_num].sync_step();
}
pub fn restart_peer(&mut self, i: usize) {
self.peers[i].restart_sync();
}
pub fn sync(&mut self) -> u32 {
self.start();
let mut total_steps = 0;
while !self.done() {
self.sync_step();
self.deliver_io_messages();
self.deliver_new_block_messages();
total_steps += 1;
}
total_steps
}
pub fn sync_steps(&mut self, count: usize) {
self.start();
for _ in 0..count {
self.sync_step();
}
}
pub fn deliver_io_messages(&mut self) {
for peer in self.peers.iter() {
peer.process_all_io_messages();
}
}
pub fn deliver_new_block_messages(&mut self) {
for peer in self.peers.iter() {
peer.process_all_new_block_messages();
}
}
pub fn done(&self) -> bool {
self.peers.iter().all(|p| p.is_done())
}
}
impl TestNet> {
pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) {
let peer = &mut self.peers[peer_id];
peer.sync.write().chain_new_blocks(&mut TestIo::new(&*peer.chain, &peer.snapshot_service, &peer.queue, None, None), &[], &[], &[], &[], &[], &[]);
}
}
pub struct TestIoHandler {
pub client: Arc,
pub private_tx_queued: Mutex,
}
impl TestIoHandler {
pub fn new(client: Arc) -> Self {
TestIoHandler {
client,
private_tx_queued: Mutex::default(),
}
}
}
impl IoHandler for TestIoHandler {
fn message(&self, _io: &IoContext, net_message: &ClientIoMessage) {
match *net_message {
ClientIoMessage::Execute(ref exec) => {
*self.private_tx_queued.lock() += 1;
(*exec.0)(&self.client);
},
_ => {} // ignore other messages
}
}
}
impl ChainNotify for EthPeer {
fn new_blocks(&self, new_blocks: NewBlocks)
{
if new_blocks.has_more_blocks_to_import { return }
let (enacted, retracted) = new_blocks.route.into_enacted_retracted();
self.new_blocks_queue.write().push_back(NewBlockMessage {
imported: new_blocks.imported,
invalid: new_blocks.invalid,
enacted,
retracted,
sealed: new_blocks.sealed,
proposed: new_blocks.proposed,
});
}
fn start(&self) {}
fn stop(&self) {}
fn broadcast(&self, message_type: ChainMessageType) {
self.io_queue.write().push_back(message_type)
}
}