Big folder refactor
This commit is contained in:
301
crates/ethcore/sync/src/tests/chain.rs
Normal file
301
crates/ethcore/sync/src/tests/chain.rs
Normal file
@@ -0,0 +1,301 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// OpenEthereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::helpers::*;
|
||||
use chain::SyncState;
|
||||
use ethcore::client::{
|
||||
BlockChainClient, BlockId, BlockInfo, ChainInfo, EachBlockWith, TestBlockChainClient,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use SyncConfig;
|
||||
use WarpSync;
|
||||
|
||||
#[test]
|
||||
fn two_peers() {
|
||||
::env_logger::try_init().ok();
|
||||
let mut net = TestNet::new(3);
|
||||
net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle);
|
||||
net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle);
|
||||
net.sync();
|
||||
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
|
||||
assert_eq!(
|
||||
*net.peer(0).chain.blocks.read(),
|
||||
*net.peer(1).chain.blocks.read()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn long_chain() {
|
||||
::env_logger::try_init().ok();
|
||||
let mut net = TestNet::new(2);
|
||||
net.peer(1).chain.add_blocks(50000, EachBlockWith::Nothing);
|
||||
net.sync();
|
||||
assert!(net.peer(0).chain.block(BlockId::Number(50000)).is_some());
|
||||
assert_eq!(
|
||||
*net.peer(0).chain.blocks.read(),
|
||||
*net.peer(1).chain.blocks.read()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_after_sync() {
|
||||
::env_logger::try_init().ok();
|
||||
let mut net = TestNet::new(3);
|
||||
net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle);
|
||||
net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle);
|
||||
net.sync();
|
||||
let status = net.peer(0).sync.read().status();
|
||||
assert_eq!(status.state, SyncState::Idle);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn takes_few_steps() {
|
||||
let mut net = TestNet::new(3);
|
||||
net.peer(1).chain.add_blocks(100, EachBlockWith::Uncle);
|
||||
net.peer(2).chain.add_blocks(100, EachBlockWith::Uncle);
|
||||
let total_steps = net.sync();
|
||||
assert!(total_steps < 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_blocks() {
|
||||
::env_logger::try_init().ok();
|
||||
let mut net = TestNet::new(3);
|
||||
for n in 0..200 {
|
||||
let with = if n % 2 == 0 {
|
||||
EachBlockWith::Nothing
|
||||
} else {
|
||||
EachBlockWith::Uncle
|
||||
};
|
||||
net.peer(1).chain.add_blocks(5, with.clone());
|
||||
net.peer(2).chain.add_blocks(5, with);
|
||||
}
|
||||
net.sync();
|
||||
assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some());
|
||||
assert_eq!(
|
||||
*net.peer(0).chain.blocks.read(),
|
||||
*net.peer(1).chain.blocks.read()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forked() {
|
||||
::env_logger::try_init().ok();
|
||||
let mut net = TestNet::new(3);
|
||||
net.peer(0).chain.add_blocks(30, EachBlockWith::Uncle);
|
||||
net.peer(1).chain.add_blocks(30, EachBlockWith::Uncle);
|
||||
net.peer(2).chain.add_blocks(30, EachBlockWith::Uncle);
|
||||
net.peer(0).chain.add_blocks(10, EachBlockWith::Nothing); //fork
|
||||
net.peer(1).chain.add_blocks(20, EachBlockWith::Uncle);
|
||||
net.peer(2).chain.add_blocks(20, EachBlockWith::Uncle);
|
||||
net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle); //fork between 1 and 2
|
||||
net.peer(2).chain.add_blocks(1, EachBlockWith::Nothing);
|
||||
// peer 1 has the best chain of 601 blocks
|
||||
let peer1_chain = net.peer(1).chain.numbers.read().clone();
|
||||
net.sync();
|
||||
assert_eq!(
|
||||
*net.peer(0).chain.difficulty.read(),
|
||||
*net.peer(1).chain.difficulty.read()
|
||||
);
|
||||
assert_eq!(&*net.peer(0).chain.numbers.read(), &peer1_chain);
|
||||
assert_eq!(&*net.peer(1).chain.numbers.read(), &peer1_chain);
|
||||
assert_eq!(&*net.peer(2).chain.numbers.read(), &peer1_chain);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forked_with_misbehaving_peer() {
|
||||
::env_logger::try_init().ok();
|
||||
let mut net = TestNet::new(3);
|
||||
|
||||
let mut alt_spec = ::ethcore::spec::Spec::new_test();
|
||||
alt_spec.extra_data = b"fork".to_vec();
|
||||
// peer 0 is on a totally different chain with higher total difficulty
|
||||
net.peer_mut(0).chain = Arc::new(TestBlockChainClient::new_with_spec(alt_spec));
|
||||
net.peer(0).chain.add_blocks(50, EachBlockWith::Nothing);
|
||||
net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing);
|
||||
net.peer(2).chain.add_blocks(10, EachBlockWith::Nothing);
|
||||
|
||||
net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing);
|
||||
net.peer(2).chain.add_blocks(20, EachBlockWith::Uncle);
|
||||
// peer 1 should sync to peer 2, others should not change
|
||||
let peer0_chain = net.peer(0).chain.numbers.read().clone();
|
||||
let peer2_chain = net.peer(2).chain.numbers.read().clone();
|
||||
net.sync();
|
||||
assert_eq!(&*net.peer(0).chain.numbers.read(), &peer0_chain);
|
||||
assert_eq!(&*net.peer(1).chain.numbers.read(), &peer2_chain);
|
||||
assert_eq!(&*net.peer(2).chain.numbers.read(), &peer2_chain);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn net_hard_fork() {
|
||||
::env_logger::try_init().ok();
|
||||
let ref_client = TestBlockChainClient::new();
|
||||
ref_client.add_blocks(50, EachBlockWith::Uncle);
|
||||
{
|
||||
let mut net = TestNet::new_with_fork(
|
||||
2,
|
||||
Some((50, ref_client.block_hash(BlockId::Number(50)).unwrap())),
|
||||
);
|
||||
net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle);
|
||||
net.sync();
|
||||
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 100);
|
||||
}
|
||||
{
|
||||
let mut net = TestNet::new_with_fork(
|
||||
2,
|
||||
Some((50, ref_client.block_hash(BlockId::Number(50)).unwrap())),
|
||||
);
|
||||
net.peer(0).chain.add_blocks(100, EachBlockWith::Nothing);
|
||||
net.sync();
|
||||
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restart() {
|
||||
::env_logger::try_init().ok();
|
||||
let mut net = TestNet::new(3);
|
||||
net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle);
|
||||
net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle);
|
||||
|
||||
net.sync();
|
||||
|
||||
// make sure that sync has actually happened
|
||||
assert!(net.peer(0).chain.chain_info().best_block_number > 100);
|
||||
net.restart_peer(0);
|
||||
|
||||
let status = net.peer(0).sync.read().status();
|
||||
assert_eq!(status.state, SyncState::Idle);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_empty() {
|
||||
let net = TestNet::new(2);
|
||||
assert_eq!(net.peer(0).sync.read().status().state, SyncState::Idle);
|
||||
let mut config = SyncConfig::default();
|
||||
config.warp_sync = WarpSync::Enabled;
|
||||
let net = TestNet::new_with_config(2, config);
|
||||
assert_eq!(
|
||||
net.peer(0).sync.read().status().state,
|
||||
SyncState::WaitingPeers
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_packet() {
|
||||
let mut net = TestNet::new(2);
|
||||
net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle);
|
||||
net.peer(1).chain.add_blocks(1, EachBlockWith::Uncle);
|
||||
|
||||
net.start();
|
||||
|
||||
net.sync_step_peer(0);
|
||||
|
||||
assert_eq!(1, net.peer(0).queue.read().len());
|
||||
assert_eq!(0x00, net.peer(0).queue.read()[0].packet_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn propagate_hashes() {
|
||||
let mut net = TestNet::new(6);
|
||||
net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle);
|
||||
net.sync();
|
||||
|
||||
net.peer(0).chain.add_blocks(10, EachBlockWith::Uncle);
|
||||
net.sync();
|
||||
net.trigger_chain_new_blocks(0); //first event just sets the marker
|
||||
net.trigger_chain_new_blocks(0);
|
||||
|
||||
// 5 peers with NewHahses, 4 with blocks
|
||||
assert_eq!(9, net.peer(0).queue.read().len());
|
||||
let mut hashes = 0;
|
||||
let mut blocks = 0;
|
||||
for i in 0..net.peer(0).queue.read().len() {
|
||||
if net.peer(0).queue.read()[i].packet_id == 0x1 {
|
||||
hashes += 1;
|
||||
}
|
||||
if net.peer(0).queue.read()[i].packet_id == 0x7 {
|
||||
blocks += 1;
|
||||
}
|
||||
}
|
||||
assert_eq!(blocks, 4);
|
||||
assert_eq!(hashes, 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn propagate_blocks() {
|
||||
let mut net = TestNet::new(20);
|
||||
net.peer(1).chain.add_blocks(10, EachBlockWith::Uncle);
|
||||
net.sync();
|
||||
|
||||
net.peer(0).chain.add_blocks(10, EachBlockWith::Uncle);
|
||||
net.trigger_chain_new_blocks(0); //first event just sets the marker
|
||||
net.trigger_chain_new_blocks(0);
|
||||
|
||||
assert!(!net.peer(0).queue.read().is_empty());
|
||||
// NEW_BLOCK_PACKET
|
||||
let blocks = net
|
||||
.peer(0)
|
||||
.queue
|
||||
.read()
|
||||
.iter()
|
||||
.filter(|p| p.packet_id == 0x7)
|
||||
.count();
|
||||
assert!(blocks > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restart_on_malformed_block() {
|
||||
::env_logger::try_init().ok();
|
||||
let mut net = TestNet::new(2);
|
||||
net.peer(1).chain.add_blocks(5, EachBlockWith::Nothing);
|
||||
net.peer(1)
|
||||
.chain
|
||||
.add_block(EachBlockWith::Nothing, |mut header| {
|
||||
header
|
||||
.set_extra_data(b"This extra data is way too long to be considered valid".to_vec());
|
||||
header
|
||||
});
|
||||
net.sync_steps(20);
|
||||
|
||||
// This gets accepted just fine since the TestBlockChainClient performs no validation.
|
||||
// Probably remove this test?
|
||||
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reject_on_broken_chain() {
|
||||
let mut net = TestNet::new(2);
|
||||
net.peer(1).chain.add_blocks(10, EachBlockWith::Nothing);
|
||||
net.peer(1).chain.corrupt_block_parent(6);
|
||||
net.sync_steps(20);
|
||||
|
||||
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disconnect_on_unrelated_chain() {
|
||||
::env_logger::try_init().ok();
|
||||
let mut net = TestNet::new(2);
|
||||
net.peer(0).chain.set_history(Some(20));
|
||||
net.peer(1).chain.set_history(Some(20));
|
||||
net.restart_peer(0);
|
||||
net.restart_peer(1);
|
||||
net.peer(0).chain.add_blocks(500, EachBlockWith::Uncle);
|
||||
net.peer(1).chain.add_blocks(300, EachBlockWith::Nothing);
|
||||
net.sync();
|
||||
assert_eq!(net.disconnect_events, vec![(0, 0)]);
|
||||
}
|
||||
174
crates/ethcore/sync/src/tests/consensus.rs
Normal file
174
crates/ethcore/sync/src/tests/consensus.rs
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// OpenEthereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::helpers::*;
|
||||
use ethcore::{
|
||||
client::{ChainInfo, ClientIoMessage},
|
||||
engines,
|
||||
miner::{self, MinerService},
|
||||
spec::Spec,
|
||||
};
|
||||
use ethereum_types::{Address, U256};
|
||||
use ethkey::{KeyPair, Secret};
|
||||
use hash::keccak;
|
||||
use io::{IoChannel, IoHandler};
|
||||
use std::sync::Arc;
|
||||
use types::transaction::{Action, PendingTransaction, Transaction, TypedTransaction};
|
||||
use SyncConfig;
|
||||
|
||||
fn new_tx(secret: &Secret, nonce: U256, chain_id: u64) -> PendingTransaction {
|
||||
let signed = TypedTransaction::Legacy(Transaction {
|
||||
nonce: nonce.into(),
|
||||
gas_price: 0.into(),
|
||||
gas: 21000.into(),
|
||||
action: Action::Call(Address::default()),
|
||||
value: 0.into(),
|
||||
data: Vec::new(),
|
||||
})
|
||||
.sign(secret, Some(chain_id));
|
||||
PendingTransaction::new(signed, None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authority_round() {
|
||||
let s0 = KeyPair::from_secret_slice(&keccak("1")).unwrap();
|
||||
let s1 = KeyPair::from_secret_slice(&keccak("0")).unwrap();
|
||||
|
||||
let chain_id = Spec::new_test_round().chain_id();
|
||||
let mut net = TestNet::with_spec(2, SyncConfig::default(), Spec::new_test_round);
|
||||
let io_handler0: Arc<dyn IoHandler<ClientIoMessage>> =
|
||||
Arc::new(TestIoHandler::new(net.peer(0).chain.clone()));
|
||||
let io_handler1: Arc<dyn IoHandler<ClientIoMessage>> =
|
||||
Arc::new(TestIoHandler::new(net.peer(1).chain.clone()));
|
||||
// Push transaction to both clients. Only one of them gets lucky to produce a block.
|
||||
net.peer(0)
|
||||
.miner
|
||||
.set_author(miner::Author::Sealer(engines::signer::from_keypair(
|
||||
s0.clone(),
|
||||
)));
|
||||
net.peer(1)
|
||||
.miner
|
||||
.set_author(miner::Author::Sealer(engines::signer::from_keypair(
|
||||
s1.clone(),
|
||||
)));
|
||||
net.peer(0)
|
||||
.chain
|
||||
.engine()
|
||||
.register_client(Arc::downgrade(&net.peer(0).chain) as _);
|
||||
net.peer(1)
|
||||
.chain
|
||||
.engine()
|
||||
.register_client(Arc::downgrade(&net.peer(1).chain) as _);
|
||||
net.peer(0)
|
||||
.chain
|
||||
.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1)));
|
||||
net.peer(1)
|
||||
.chain
|
||||
.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0)));
|
||||
// exchange statuses
|
||||
net.sync();
|
||||
// Trigger block proposal
|
||||
net.peer(0)
|
||||
.miner
|
||||
.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), chain_id))
|
||||
.unwrap();
|
||||
net.peer(1)
|
||||
.miner
|
||||
.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), chain_id))
|
||||
.unwrap();
|
||||
// Sync a block
|
||||
net.sync();
|
||||
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1);
|
||||
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1);
|
||||
|
||||
net.peer(0)
|
||||
.miner
|
||||
.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), chain_id))
|
||||
.unwrap();
|
||||
net.peer(1)
|
||||
.miner
|
||||
.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), chain_id))
|
||||
.unwrap();
|
||||
// Move to next proposer step.
|
||||
net.peer(0).chain.engine().step();
|
||||
net.peer(1).chain.engine().step();
|
||||
net.sync();
|
||||
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 2);
|
||||
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2);
|
||||
|
||||
// Fork the network with equal height.
|
||||
net.peer(0)
|
||||
.miner
|
||||
.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), chain_id))
|
||||
.unwrap();
|
||||
net.peer(1)
|
||||
.miner
|
||||
.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), chain_id))
|
||||
.unwrap();
|
||||
// Let both nodes build one block.
|
||||
net.peer(0).chain.engine().step();
|
||||
let early_hash = net.peer(0).chain.chain_info().best_block_hash;
|
||||
net.peer(1).chain.engine().step();
|
||||
net.peer(0).chain.engine().step();
|
||||
net.peer(1).chain.engine().step();
|
||||
let ci0 = net.peer(0).chain.chain_info();
|
||||
let ci1 = net.peer(1).chain.chain_info();
|
||||
assert_eq!(ci0.best_block_number, 3);
|
||||
assert_eq!(ci1.best_block_number, 3);
|
||||
assert!(ci0.best_block_hash != ci1.best_block_hash);
|
||||
// Reorg to the chain with earlier view.
|
||||
net.sync();
|
||||
let ci0 = net.peer(0).chain.chain_info();
|
||||
let ci1 = net.peer(1).chain.chain_info();
|
||||
assert_eq!(ci0.best_block_number, 3);
|
||||
assert_eq!(ci1.best_block_number, 3);
|
||||
assert_eq!(ci0.best_block_hash, ci1.best_block_hash);
|
||||
assert_eq!(ci1.best_block_hash, early_hash);
|
||||
|
||||
// Selfish miner
|
||||
net.peer(0)
|
||||
.miner
|
||||
.import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into(), chain_id))
|
||||
.unwrap();
|
||||
net.peer(1)
|
||||
.miner
|
||||
.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into(), chain_id))
|
||||
.unwrap();
|
||||
// Node 0 is an earlier primary.
|
||||
net.peer(0).chain.engine().step();
|
||||
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4);
|
||||
net.peer(0).chain.engine().step();
|
||||
net.peer(0).chain.engine().step();
|
||||
net.peer(0).chain.engine().step();
|
||||
assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4);
|
||||
// Node 1 makes 2 blocks, but is a later primary on the first one.
|
||||
net.peer(1).chain.engine().step();
|
||||
net.peer(1).chain.engine().step();
|
||||
net.peer(1)
|
||||
.miner
|
||||
.import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into(), chain_id))
|
||||
.unwrap();
|
||||
net.peer(1).chain.engine().step();
|
||||
net.peer(1).chain.engine().step();
|
||||
assert_eq!(net.peer(1).chain.chain_info().best_block_number, 5);
|
||||
// Reorg to the longest chain one not ealier view one.
|
||||
net.sync();
|
||||
let ci0 = net.peer(0).chain.chain_info();
|
||||
let ci1 = net.peer(1).chain.chain_info();
|
||||
assert_eq!(ci0.best_block_number, 5);
|
||||
assert_eq!(ci1.best_block_number, 5);
|
||||
assert_eq!(ci0.best_block_hash, ci1.best_block_hash);
|
||||
}
|
||||
618
crates/ethcore/sync/src/tests/helpers.rs
Normal file
618
crates/ethcore/sync/src/tests/helpers.rs
Normal file
@@ -0,0 +1,618 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// OpenEthereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use api::PAR_PROTOCOL;
|
||||
use bytes::Bytes;
|
||||
use chain::{
|
||||
sync_packet::{PacketInfo, SyncPacket},
|
||||
ChainSync, ForkFilterApi, SyncSupplier, ETH_PROTOCOL_VERSION_64, PAR_PROTOCOL_VERSION_2,
|
||||
};
|
||||
use ethcore::{
|
||||
client::{
|
||||
BlockChainClient, ChainMessageType, ChainNotify, Client as EthcoreClient, ClientConfig,
|
||||
ClientIoMessage, NewBlocks, TestBlockChainClient,
|
||||
},
|
||||
miner::Miner,
|
||||
snapshot::SnapshotService,
|
||||
spec::Spec,
|
||||
test_helpers,
|
||||
};
|
||||
|
||||
use ethereum_types::H256;
|
||||
use io::{IoChannel, IoContext, IoHandler};
|
||||
use network::{self, client_version::ClientVersion, PacketId, PeerId, ProtocolId, SessionInfo};
|
||||
use parking_lot::RwLock;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
sync::Arc,
|
||||
};
|
||||
use sync_io::SyncIo;
|
||||
use tests::snapshot::*;
|
||||
|
||||
use types::BlockNumber;
|
||||
use SyncConfig;
|
||||
|
||||
pub trait FlushingBlockChainClient: BlockChainClient {
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
impl FlushingBlockChainClient for EthcoreClient {
|
||||
fn flush(&self) {
|
||||
self.flush_queue();
|
||||
}
|
||||
}
|
||||
|
||||
impl FlushingBlockChainClient for TestBlockChainClient {}
|
||||
|
||||
pub struct TestIo<'p, C>
|
||||
where
|
||||
C: FlushingBlockChainClient,
|
||||
C: 'p,
|
||||
{
|
||||
pub chain: &'p C,
|
||||
pub snapshot_service: &'p TestSnapshotService,
|
||||
pub queue: &'p RwLock<VecDeque<TestPacket>>,
|
||||
pub sender: Option<PeerId>,
|
||||
pub to_disconnect: HashSet<PeerId>,
|
||||
pub packets: Vec<TestPacket>,
|
||||
pub peers_info: HashMap<PeerId, String>,
|
||||
overlay: RwLock<HashMap<BlockNumber, Bytes>>,
|
||||
}
|
||||
|
||||
impl<'p, C> TestIo<'p, C>
|
||||
where
|
||||
C: FlushingBlockChainClient,
|
||||
C: 'p,
|
||||
{
|
||||
pub fn new(
|
||||
chain: &'p C,
|
||||
ss: &'p TestSnapshotService,
|
||||
queue: &'p RwLock<VecDeque<TestPacket>>,
|
||||
sender: Option<PeerId>,
|
||||
) -> TestIo<'p, C> {
|
||||
TestIo {
|
||||
chain: chain,
|
||||
snapshot_service: ss,
|
||||
queue: queue,
|
||||
sender: sender,
|
||||
to_disconnect: HashSet::new(),
|
||||
overlay: RwLock::new(HashMap::new()),
|
||||
packets: Vec::new(),
|
||||
peers_info: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, C> Drop for TestIo<'p, C>
|
||||
where
|
||||
C: FlushingBlockChainClient,
|
||||
C: 'p,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.queue.write().extend(self.packets.drain(..));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, C> SyncIo for TestIo<'p, C>
|
||||
where
|
||||
C: FlushingBlockChainClient,
|
||||
C: 'p,
|
||||
{
|
||||
fn disable_peer(&mut self, peer_id: PeerId) {
|
||||
self.disconnect_peer(peer_id);
|
||||
}
|
||||
|
||||
fn disconnect_peer(&mut self, peer_id: PeerId) {
|
||||
self.to_disconnect.insert(peer_id);
|
||||
}
|
||||
|
||||
fn is_expired(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn respond(&mut self, packet_id: PacketId, data: Vec<u8>) -> Result<(), network::Error> {
|
||||
self.packets.push(TestPacket {
|
||||
data: data,
|
||||
packet_id: packet_id,
|
||||
recipient: self.sender.unwrap(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
packet_id: SyncPacket,
|
||||
data: Vec<u8>,
|
||||
) -> Result<(), network::Error> {
|
||||
self.packets.push(TestPacket {
|
||||
data: data,
|
||||
packet_id: packet_id.id(),
|
||||
recipient: peer_id,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn chain(&self) -> &dyn BlockChainClient {
|
||||
&*self.chain
|
||||
}
|
||||
|
||||
fn peer_version(&self, peer_id: PeerId) -> ClientVersion {
|
||||
let client_id = self
|
||||
.peers_info
|
||||
.get(&peer_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| peer_id.to_string());
|
||||
|
||||
ClientVersion::from(client_id)
|
||||
}
|
||||
|
||||
fn snapshot_service(&self) -> &dyn SnapshotService {
|
||||
self.snapshot_service
|
||||
}
|
||||
|
||||
fn peer_session_info(&self, _peer_id: PeerId) -> Option<SessionInfo> {
|
||||
None
|
||||
}
|
||||
|
||||
fn protocol_version(&self, protocol: &ProtocolId, _peer_id: PeerId) -> u8 {
|
||||
if protocol == &PAR_PROTOCOL {
|
||||
PAR_PROTOCOL_VERSION_2.0
|
||||
} else {
|
||||
ETH_PROTOCOL_VERSION_64.0
|
||||
}
|
||||
}
|
||||
|
||||
fn chain_overlay(&self) -> &RwLock<HashMap<BlockNumber, Bytes>> {
|
||||
&self.overlay
|
||||
}
|
||||
}
|
||||
|
||||
/// Mock for emulution of async run of new blocks
|
||||
struct NewBlockMessage {
|
||||
imported: Vec<H256>,
|
||||
invalid: Vec<H256>,
|
||||
enacted: Vec<H256>,
|
||||
retracted: Vec<H256>,
|
||||
sealed: Vec<H256>,
|
||||
proposed: Vec<Bytes>,
|
||||
}
|
||||
|
||||
/// Abstract messages between peers.
|
||||
pub trait Message {
|
||||
/// The intended recipient of this message.
|
||||
fn recipient(&self) -> PeerId;
|
||||
}
|
||||
|
||||
/// Mock subprotocol packet
|
||||
pub struct TestPacket {
|
||||
pub data: Bytes,
|
||||
pub packet_id: PacketId,
|
||||
pub recipient: PeerId,
|
||||
}
|
||||
|
||||
impl Message for TestPacket {
|
||||
fn recipient(&self) -> PeerId {
|
||||
self.recipient
|
||||
}
|
||||
}
|
||||
|
||||
/// A peer which can be a member of the `TestNet`.
|
||||
pub trait Peer {
|
||||
type Message: Message;
|
||||
|
||||
/// Called on connection to other indicated peer.
|
||||
fn on_connect(&self, other: PeerId);
|
||||
|
||||
/// Called on disconnect from other indicated peer.
|
||||
fn on_disconnect(&self, other: PeerId);
|
||||
|
||||
/// Receive a message from another peer. Return a set of peers to disconnect.
|
||||
fn receive_message(&self, from: PeerId, msg: Self::Message) -> HashSet<PeerId>;
|
||||
|
||||
/// Produce the next pending message to send to another peer.
|
||||
fn pending_message(&self) -> Option<Self::Message>;
|
||||
|
||||
/// Whether this peer is done syncing (has no messages to send).
|
||||
fn is_done(&self) -> bool;
|
||||
|
||||
/// Execute a "sync step". This is called for each peer after it sends a packet.
|
||||
fn sync_step(&self);
|
||||
|
||||
/// Restart sync for a peer.
|
||||
fn restart_sync(&self);
|
||||
|
||||
/// Process the queue of pending io messages
|
||||
fn process_all_io_messages(&self);
|
||||
|
||||
/// Process the queue of new block messages
|
||||
fn process_all_new_block_messages(&self);
|
||||
}
|
||||
|
||||
pub struct EthPeer<C>
|
||||
where
|
||||
C: FlushingBlockChainClient,
|
||||
{
|
||||
pub chain: Arc<C>,
|
||||
pub miner: Arc<Miner>,
|
||||
pub snapshot_service: Arc<TestSnapshotService>,
|
||||
pub sync: RwLock<ChainSync>,
|
||||
pub queue: RwLock<VecDeque<TestPacket>>,
|
||||
pub io_queue: RwLock<VecDeque<ChainMessageType>>,
|
||||
new_blocks_queue: RwLock<VecDeque<NewBlockMessage>>,
|
||||
}
|
||||
|
||||
impl<C> EthPeer<C>
|
||||
where
|
||||
C: FlushingBlockChainClient,
|
||||
{
|
||||
fn is_io_queue_empty(&self) -> bool {
|
||||
self.io_queue.read().is_empty()
|
||||
}
|
||||
|
||||
fn is_new_blocks_queue_empty(&self) -> bool {
|
||||
self.new_blocks_queue.read().is_empty()
|
||||
}
|
||||
|
||||
fn process_io_message(&self, message: ChainMessageType) {
|
||||
let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None);
|
||||
match message {
|
||||
ChainMessageType::Consensus(data) => {
|
||||
self.sync.write().propagate_consensus_packet(&mut io, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_new_block_message(&self, message: NewBlockMessage) {
|
||||
let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None);
|
||||
self.sync.write().chain_new_blocks(
|
||||
&mut io,
|
||||
&message.imported,
|
||||
&message.invalid,
|
||||
&message.enacted,
|
||||
&message.retracted,
|
||||
&message.sealed,
|
||||
&message.proposed,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: FlushingBlockChainClient> Peer for EthPeer<C> {
|
||||
type Message = TestPacket;
|
||||
|
||||
fn on_connect(&self, other: PeerId) {
|
||||
self.sync.write().update_targets(&*self.chain);
|
||||
self.sync.write().on_peer_connected(
|
||||
&mut TestIo::new(
|
||||
&*self.chain,
|
||||
&self.snapshot_service,
|
||||
&self.queue,
|
||||
Some(other),
|
||||
),
|
||||
other,
|
||||
);
|
||||
}
|
||||
|
||||
fn on_disconnect(&self, other: PeerId) {
|
||||
let mut io = TestIo::new(
|
||||
&*self.chain,
|
||||
&self.snapshot_service,
|
||||
&self.queue,
|
||||
Some(other),
|
||||
);
|
||||
self.sync.write().on_peer_aborting(&mut io, other);
|
||||
}
|
||||
|
||||
fn receive_message(&self, from: PeerId, msg: TestPacket) -> HashSet<PeerId> {
|
||||
let mut io = TestIo::new(
|
||||
&*self.chain,
|
||||
&self.snapshot_service,
|
||||
&self.queue,
|
||||
Some(from),
|
||||
);
|
||||
SyncSupplier::dispatch_packet(&self.sync, &mut io, from, msg.packet_id, &msg.data);
|
||||
self.chain.flush();
|
||||
io.to_disconnect.clone()
|
||||
}
|
||||
|
||||
fn pending_message(&self) -> Option<TestPacket> {
|
||||
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.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,
|
||||
));
|
||||
}
|
||||
|
||||
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<P> {
|
||||
pub peers: Vec<Arc<P>>,
|
||||
pub started: bool,
|
||||
pub disconnect_events: Vec<(PeerId, PeerId)>, //disconnected (initiated by, to)
|
||||
}
|
||||
|
||||
impl TestNet<EthPeer<TestBlockChainClient>> {
|
||||
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 sync = ChainSync::new(config.clone(), &chain, ForkFilterApi::new_dummy(&chain));
|
||||
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()),
|
||||
io_queue: RwLock::new(VecDeque::new()),
|
||||
new_blocks_queue: RwLock::new(VecDeque::new()),
|
||||
}));
|
||||
}
|
||||
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<TestBlockChainClient> {
|
||||
Arc::get_mut(&mut self.peers[i]).expect("Arc never exposed externally")
|
||||
}
|
||||
}
|
||||
|
||||
impl TestNet<EthPeer<EthcoreClient>> {
|
||||
pub fn with_spec<F>(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 ss = Arc::new(TestSnapshotService::new());
|
||||
let sync = ChainSync::new(config, &*client, ForkFilterApi::new_dummy(&*client));
|
||||
let peer = Arc::new(EthPeer {
|
||||
sync: RwLock::new(sync),
|
||||
snapshot_service: ss,
|
||||
chain: client,
|
||||
miner,
|
||||
queue: RwLock::new(VecDeque::new()),
|
||||
io_queue: RwLock::new(VecDeque::new()),
|
||||
new_blocks_queue: RwLock::new(VecDeque::new()),
|
||||
});
|
||||
peer.chain.add_notify(peer.clone());
|
||||
//private_provider.add_notify(peer.clone());
|
||||
self.peers.push(peer);
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> TestNet<P>
|
||||
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<C: FlushingBlockChainClient> TestNet<EthPeer<C>> {
|
||||
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),
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestIoHandler {
|
||||
pub client: Arc<EthcoreClient>,
|
||||
}
|
||||
|
||||
impl TestIoHandler {
|
||||
pub fn new(client: Arc<EthcoreClient>) -> Self {
|
||||
TestIoHandler { client }
|
||||
}
|
||||
}
|
||||
|
||||
impl IoHandler<ClientIoMessage> for TestIoHandler {
|
||||
fn message(&self, _io: &IoContext<ClientIoMessage>, net_message: &ClientIoMessage) {
|
||||
match *net_message {
|
||||
ClientIoMessage::Execute(ref exec) => {
|
||||
(*exec.0)(&self.client);
|
||||
}
|
||||
_ => {} // ignore other messages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChainNotify for EthPeer<EthcoreClient> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
23
crates/ethcore/sync/src/tests/mod.rs
Normal file
23
crates/ethcore/sync/src/tests/mod.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// OpenEthereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
mod chain;
|
||||
mod consensus;
|
||||
pub mod helpers;
|
||||
pub mod snapshot;
|
||||
|
||||
#[cfg(feature = "ipc")]
|
||||
mod rpc;
|
||||
29
crates/ethcore/sync/src/tests/rpc.rs
Normal file
29
crates/ethcore/sync/src/tests/rpc.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// OpenEthereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::super::NetworkConfiguration;
|
||||
use ipc::binary::{deserialize, serialize};
|
||||
use network::NetworkConfiguration as BasicNetworkConfiguration;
|
||||
use std::convert::From;
|
||||
|
||||
#[test]
|
||||
fn network_settings_serialize() {
|
||||
let net_cfg = NetworkConfiguration::from(BasicNetworkConfiguration::new_local());
|
||||
let serialized = serialize(&net_cfg).unwrap();
|
||||
let deserialized = deserialize::<NetworkConfiguration>(&serialized).unwrap();
|
||||
|
||||
assert_eq!(net_cfg.udp_port, deserialized.udp_port);
|
||||
}
|
||||
221
crates/ethcore/sync/src/tests/snapshot.rs
Normal file
221
crates/ethcore/sync/src/tests/snapshot.rs
Normal file
@@ -0,0 +1,221 @@
|
||||
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
|
||||
// This file is part of OpenEthereum.
|
||||
|
||||
// OpenEthereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// OpenEthereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::helpers::*;
|
||||
use bytes::Bytes;
|
||||
use ethcore::{
|
||||
client::EachBlockWith,
|
||||
snapshot::{CreationStatus, ManifestData, RestorationStatus, SnapshotService},
|
||||
};
|
||||
use ethereum_types::H256;
|
||||
use hash::keccak;
|
||||
use parking_lot::Mutex;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use types::BlockNumber;
|
||||
use SyncConfig;
|
||||
use WarpSync;
|
||||
|
||||
pub struct TestSnapshotService {
|
||||
manifest: Option<ManifestData>,
|
||||
chunks: HashMap<H256, Bytes>,
|
||||
|
||||
restoration_manifest: Mutex<Option<ManifestData>>,
|
||||
state_restoration_chunks: Mutex<HashMap<H256, Bytes>>,
|
||||
block_restoration_chunks: Mutex<HashMap<H256, Bytes>>,
|
||||
}
|
||||
|
||||
impl TestSnapshotService {
|
||||
pub fn new() -> TestSnapshotService {
|
||||
TestSnapshotService {
|
||||
manifest: None,
|
||||
chunks: HashMap::new(),
|
||||
restoration_manifest: Mutex::new(None),
|
||||
state_restoration_chunks: Mutex::new(HashMap::new()),
|
||||
block_restoration_chunks: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_snapshot(
|
||||
num_chunks: usize,
|
||||
block_hash: H256,
|
||||
block_number: BlockNumber,
|
||||
) -> TestSnapshotService {
|
||||
let num_state_chunks = num_chunks / 2;
|
||||
let num_block_chunks = num_chunks - num_state_chunks;
|
||||
let state_chunks: Vec<Bytes> = (0..num_state_chunks)
|
||||
.map(|_| H256::random().to_vec())
|
||||
.collect();
|
||||
let block_chunks: Vec<Bytes> = (0..num_block_chunks)
|
||||
.map(|_| H256::random().to_vec())
|
||||
.collect();
|
||||
let manifest = ManifestData {
|
||||
version: 2,
|
||||
state_hashes: state_chunks.iter().map(|data| keccak(data)).collect(),
|
||||
block_hashes: block_chunks.iter().map(|data| keccak(data)).collect(),
|
||||
state_root: H256::new(),
|
||||
block_number: block_number,
|
||||
block_hash: block_hash,
|
||||
};
|
||||
let mut chunks: HashMap<H256, Bytes> = state_chunks
|
||||
.into_iter()
|
||||
.map(|data| (keccak(&data), data))
|
||||
.collect();
|
||||
chunks.extend(block_chunks.into_iter().map(|data| (keccak(&data), data)));
|
||||
TestSnapshotService {
|
||||
manifest: Some(manifest),
|
||||
chunks: chunks,
|
||||
restoration_manifest: Mutex::new(None),
|
||||
state_restoration_chunks: Mutex::new(HashMap::new()),
|
||||
block_restoration_chunks: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SnapshotService for TestSnapshotService {
|
||||
fn manifest(&self) -> Option<ManifestData> {
|
||||
self.manifest.as_ref().cloned()
|
||||
}
|
||||
|
||||
fn supported_versions(&self) -> Option<(u64, u64)> {
|
||||
Some((1, 2))
|
||||
}
|
||||
|
||||
fn completed_chunks(&self) -> Option<Vec<H256>> {
|
||||
Some(vec![])
|
||||
}
|
||||
|
||||
fn chunk(&self, hash: H256) -> Option<Bytes> {
|
||||
self.chunks.get(&hash).cloned()
|
||||
}
|
||||
|
||||
fn creation_status(&self) -> CreationStatus {
|
||||
CreationStatus::Inactive
|
||||
}
|
||||
|
||||
fn restoration_status(&self) -> RestorationStatus {
|
||||
match *self.restoration_manifest.lock() {
|
||||
Some(ref manifest)
|
||||
if self.state_restoration_chunks.lock().len() == manifest.state_hashes.len()
|
||||
&& self.block_restoration_chunks.lock().len()
|
||||
== manifest.block_hashes.len() =>
|
||||
{
|
||||
RestorationStatus::Inactive
|
||||
}
|
||||
Some(ref manifest) => RestorationStatus::Ongoing {
|
||||
block_number: 0,
|
||||
state_chunks: manifest.state_hashes.len() as u32,
|
||||
block_chunks: manifest.block_hashes.len() as u32,
|
||||
state_chunks_done: self.state_restoration_chunks.lock().len() as u32,
|
||||
block_chunks_done: self.block_restoration_chunks.lock().len() as u32,
|
||||
},
|
||||
None => RestorationStatus::Inactive,
|
||||
}
|
||||
}
|
||||
|
||||
fn begin_restore(&self, manifest: ManifestData) {
|
||||
let mut restoration_manifest = self.restoration_manifest.lock();
|
||||
|
||||
if let Some(ref c_manifest) = *restoration_manifest {
|
||||
if c_manifest.state_root == manifest.state_root {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*restoration_manifest = Some(manifest);
|
||||
self.state_restoration_chunks.lock().clear();
|
||||
self.block_restoration_chunks.lock().clear();
|
||||
}
|
||||
|
||||
fn abort_restore(&self) {
|
||||
*self.restoration_manifest.lock() = None;
|
||||
self.state_restoration_chunks.lock().clear();
|
||||
self.block_restoration_chunks.lock().clear();
|
||||
}
|
||||
|
||||
fn abort_snapshot(&self) {}
|
||||
|
||||
fn restore_state_chunk(&self, hash: H256, chunk: Bytes) {
|
||||
if self
|
||||
.restoration_manifest
|
||||
.lock()
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.state_hashes.iter().any(|h| h == &hash))
|
||||
{
|
||||
self.state_restoration_chunks.lock().insert(hash, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
fn restore_block_chunk(&self, hash: H256, chunk: Bytes) {
|
||||
if self
|
||||
.restoration_manifest
|
||||
.lock()
|
||||
.as_ref()
|
||||
.map_or(false, |m| m.block_hashes.iter().any(|h| h == &hash))
|
||||
{
|
||||
self.block_restoration_chunks.lock().insert(hash, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
fn shutdown(&self) {
|
||||
self.abort_restore();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snapshot_sync() {
|
||||
::env_logger::try_init().ok();
|
||||
let mut config = SyncConfig::default();
|
||||
config.warp_sync = WarpSync::Enabled;
|
||||
let mut net = TestNet::new_with_config(5, config);
|
||||
let snapshot_service = Arc::new(TestSnapshotService::new_with_snapshot(
|
||||
16,
|
||||
H256::new(),
|
||||
500000,
|
||||
));
|
||||
for i in 0..4 {
|
||||
net.peer_mut(i).snapshot_service = snapshot_service.clone();
|
||||
net.peer(i).chain.add_blocks(1, EachBlockWith::Nothing);
|
||||
}
|
||||
net.sync_steps(50);
|
||||
assert_eq!(
|
||||
net.peer(4)
|
||||
.snapshot_service
|
||||
.state_restoration_chunks
|
||||
.lock()
|
||||
.len(),
|
||||
net.peer(0)
|
||||
.snapshot_service
|
||||
.manifest
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.state_hashes
|
||||
.len()
|
||||
);
|
||||
assert_eq!(
|
||||
net.peer(4)
|
||||
.snapshot_service
|
||||
.block_restoration_chunks
|
||||
.lock()
|
||||
.len(),
|
||||
net.peer(0)
|
||||
.snapshot_service
|
||||
.manifest
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.block_hashes
|
||||
.len()
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user