* cargo upgrade "ethereum-types" --all --allow-prerelease * [ethash] fix compilation errors * [ethkey] fix compilation errors * [journaldb] fix compilation errors * [dir] fix compilation errors * [ethabi] update to 0.7 * wip * [eip-712] fix compilation errors * [ethjson] fix compilation errors * [Cargo.toml] add TODO to remove patches * [ethstore] fix compilation errors * use patched keccak-hash with new primitive-types * wip * [ethcore-network-devp2p] fix compilation errors * [vm] fix compilation errors * [common-types, evm, wasm] fix compilation errors * [ethcore-db] Require AsRef instead of Deref for keys * [ethcore-blockchain] fix some compilation errors * [blooms-db] fix compilation errors Thanks a lot @dvdplm :) * we don't need no rlp ethereum feature * [ethcore] fix some compilation errors * [parity-ipfs-api] fix compilation error * [ethcore-light] fix compilation errors * [Cargo.lock] update parity-common * [ethcore-private-tx] fix some compilation errors * wip * [ethcore-private-tx] fix compilation errors * [parity-updater] fix compilation errors * [parity-rpc] fix compilation errors * [parity-bin] fix other compilation errors * update to new ethereum-types * update keccak-hash * [fastmap] fix compilation in tests * [blooms-db] fix compilation in tests * [common-types] fix compilation in tests * [triehash-ethereum] fix compilation in tests * [ethkey] fix compilation in tests * [pwasm-run-test] fix compilation errors * [wasm] fix compilation errors * [ethjson] fix compilation in tests * [eip-712] fix compilation in tests * [ethcore-blockchain] fix compilation in tests * [ethstore] fix compilation in tests * [ethstore-accounts] fix compilation in tests * [parity-hash-fetch] fix compilation in tests * [parity-whisper] fix compilation in tests * [ethcore-miner] fix compilation in tests * [ethcore-network-devp2p] fix compilation in tests * [*] upgrade rand to 0.6 * [evm] get rid of num-bigint conversions * [ethcore] downgrade trie-standardmap and criterion * [ethcore] fix some warnings * [ethcore] fix compilation in tests * [evmbin] fix compilation in tests * [updater] fix compilation in tests * [ethash] fix compilation in tests * [ethcore-secretstore] fix compilation in tests * [ethcore-sync] fix compilation in tests * [parity-rpc] fix compilation in tests * [ethcore] finally fix compilation in tests FUCK YEAH!!! * [ethstore] lazy_static is unused * [ethcore] fix test * fix up bad merge * [Cargo.toml] remove unused patches * [*] replace some git dependencies with crates.io * [Cargo.toml] remove unused lazy_static * [*] clean up * [ethcore] fix transaction_filter_deprecated test * [private-tx] fix serialization tests * fix more serialization tests * [ethkey] fix smoky test * [rpc] fix tests, please? * [ethcore] remove commented out code * Apply suggestions from code review Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * [ethstore] remove unused dev-dependency * [ethcore] remove resolved TODO * [*] resolve keccak-hash TODO * [*] s/Address::default()/Address::zero() * [rpc] remove Subscribers::new_test * [rpc] remove EthPubSubClient::new_test * [ethcore] use trie-standardmap from crates.io * [dir] fix db_root_path * [ethcore] simplify snapshot::tests::helpers::fill_storage * Apply suggestions from code review Co-Authored-By: David <dvdplm@gmail.com> * [ethcore-secretstore] resolve TODO in serialization * [ethcore-network-devp2p] resolve TODO in save_key * [Cargo.lock] update triehash * [*] use ethabi from crates.io * [ethkey] use secp256k1 from master branch * [Cargo.lock] update eth-secp256k1
815 lines
23 KiB
Rust
815 lines
23 KiB
Rust
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
|
// This file is part of Parity Ethereum.
|
|
|
|
// Parity Ethereum 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.
|
|
|
|
// Parity Ethereum 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//! Tests for the `LightProtocol` implementation.
|
|
//! These don't test of the higher level logic on top of
|
|
|
|
use common_types::blockchain_info::BlockChainInfo;
|
|
use common_types::encoded;
|
|
use common_types::ids::BlockId;
|
|
use common_types::transaction::{Action, PendingTransaction};
|
|
use ethcore::client::{EachBlockWith, TestBlockChainClient};
|
|
use ethereum_types::{H256, U256, Address, BigEndianHash};
|
|
use net::context::IoContext;
|
|
use net::load_timer::MOVING_SAMPLE_SIZE;
|
|
use net::status::{Capabilities, Status};
|
|
use net::{LightProtocol, Params, packet, Peer, Statistics};
|
|
use network::{PeerId, NodeId};
|
|
use provider::Provider;
|
|
use request::*;
|
|
use request;
|
|
use rlp::{Rlp, RlpStream};
|
|
|
|
use std::sync::Arc;
|
|
use std::time::Instant;
|
|
|
|
// helper for encoding a single request into a packet.
|
|
// panics on bad backreference.
|
|
fn encode_single(request: Request) -> NetworkRequests {
|
|
let mut builder = Builder::default();
|
|
builder.push(request).unwrap();
|
|
builder.build()
|
|
}
|
|
|
|
// helper for making a packet out of `Requests`.
|
|
fn make_packet(req_id: usize, requests: &NetworkRequests) -> Vec<u8> {
|
|
let mut stream = RlpStream::new_list(2);
|
|
stream.append(&req_id).append_list(&requests.requests());
|
|
stream.out()
|
|
}
|
|
|
|
// expected result from a call.
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
enum Expect {
|
|
/// Expect to have message sent to peer.
|
|
Send(PeerId, u8, Vec<u8>),
|
|
/// Expect this response.
|
|
Respond(u8, Vec<u8>),
|
|
/// Expect a punishment (disconnect/disable)
|
|
Punish(PeerId),
|
|
/// Expect nothing.
|
|
Nothing,
|
|
}
|
|
|
|
impl IoContext for Expect {
|
|
fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec<u8>) {
|
|
assert_eq!(self, &Expect::Send(peer, packet_id, packet_body));
|
|
}
|
|
|
|
fn respond(&self, packet_id: u8, packet_body: Vec<u8>) {
|
|
assert_eq!(self, &Expect::Respond(packet_id, packet_body));
|
|
}
|
|
|
|
fn disconnect_peer(&self, peer: PeerId) {
|
|
assert_eq!(self, &Expect::Punish(peer));
|
|
}
|
|
|
|
fn disable_peer(&self, peer: PeerId) {
|
|
assert_eq!(self, &Expect::Punish(peer));
|
|
}
|
|
|
|
fn protocol_version(&self, _peer: PeerId) -> Option<u8> {
|
|
Some(super::MAX_PROTOCOL_VERSION)
|
|
}
|
|
|
|
fn persistent_peer_id(&self, _peer: PeerId) -> Option<NodeId> {
|
|
None
|
|
}
|
|
|
|
fn is_reserved_peer(&self, peer: PeerId) -> bool {
|
|
peer == 0xff
|
|
}
|
|
}
|
|
|
|
// can't implement directly for Arc due to cross-crate orphan rules.
|
|
struct TestProvider(Arc<TestProviderInner>);
|
|
|
|
struct TestProviderInner {
|
|
client: TestBlockChainClient,
|
|
}
|
|
|
|
impl Provider for TestProvider {
|
|
fn chain_info(&self) -> BlockChainInfo {
|
|
self.0.client.chain_info()
|
|
}
|
|
|
|
fn reorg_depth(&self, a: &H256, b: &H256) -> Option<u64> {
|
|
self.0.client.reorg_depth(a, b)
|
|
}
|
|
|
|
fn earliest_state(&self) -> Option<u64> {
|
|
None
|
|
}
|
|
|
|
fn block_header(&self, id: BlockId) -> Option<encoded::Header> {
|
|
self.0.client.block_header(id)
|
|
}
|
|
|
|
fn transaction_index(&self, req: request::CompleteTransactionIndexRequest)
|
|
-> Option<request::TransactionIndexResponse>
|
|
{
|
|
Some(request::TransactionIndexResponse {
|
|
num: 100,
|
|
hash: req.hash,
|
|
index: 55,
|
|
})
|
|
}
|
|
|
|
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse> {
|
|
self.0.client.block_body(req)
|
|
}
|
|
|
|
fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse> {
|
|
self.0.client.block_receipts(req)
|
|
}
|
|
|
|
fn account_proof(&self, req: request::CompleteAccountRequest) -> Option<request::AccountResponse> {
|
|
// sort of a leaf node
|
|
let mut stream = RlpStream::new_list(2);
|
|
stream.append(&req.address_hash).append_empty_data();
|
|
Some(AccountResponse {
|
|
proof: vec![stream.out()],
|
|
balance: 10.into(),
|
|
nonce: 100.into(),
|
|
code_hash: Default::default(),
|
|
storage_root: Default::default(),
|
|
})
|
|
}
|
|
|
|
fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option<request::StorageResponse> {
|
|
Some(StorageResponse {
|
|
proof: vec![::rlp::encode(&req.key_hash)],
|
|
value: req.key_hash | req.address_hash,
|
|
})
|
|
}
|
|
|
|
fn contract_code(&self, req: request::CompleteCodeRequest) -> Option<request::CodeResponse> {
|
|
Some(CodeResponse {
|
|
code: req.block_hash.as_bytes().iter().chain(req.code_hash.as_bytes().iter()).cloned().collect(),
|
|
})
|
|
}
|
|
|
|
fn header_proof(&self, _req: request::CompleteHeaderProofRequest) -> Option<request::HeaderProofResponse> {
|
|
None
|
|
}
|
|
|
|
fn transaction_proof(&self, _req: request::CompleteExecutionRequest) -> Option<request::ExecutionResponse> {
|
|
None
|
|
}
|
|
|
|
fn epoch_signal(&self, _req: request::CompleteSignalRequest) -> Option<request::SignalResponse> {
|
|
Some(request::SignalResponse {
|
|
signal: vec![1, 2, 3, 4],
|
|
})
|
|
}
|
|
|
|
fn transactions_to_propagate(&self) -> Vec<PendingTransaction> {
|
|
self.0.client.transactions_to_propagate()
|
|
}
|
|
}
|
|
|
|
fn capabilities() -> Capabilities {
|
|
Capabilities {
|
|
serve_headers: true,
|
|
serve_chain_since: Some(1),
|
|
serve_state_since: Some(1),
|
|
tx_relay: true,
|
|
}
|
|
}
|
|
|
|
fn write_handshake(status: &Status, capabilities: &Capabilities, proto: &LightProtocol) -> Vec<u8> {
|
|
let flow_params = proto.flow_params.read().clone();
|
|
::net::status::write_handshake(status, capabilities, Some(&*flow_params))
|
|
}
|
|
|
|
fn write_free_handshake(status: &Status, capabilities: &Capabilities, proto: &LightProtocol) -> Vec<u8> {
|
|
::net::status::write_handshake(status, capabilities, Some(&proto.free_flow_params))
|
|
}
|
|
|
|
// helper for setting up the protocol handler and provider.
|
|
fn setup(capabilities: Capabilities) -> (Arc<TestProviderInner>, LightProtocol) {
|
|
let provider = Arc::new(TestProviderInner {
|
|
client: TestBlockChainClient::new(),
|
|
});
|
|
|
|
let proto = LightProtocol::new(Arc::new(TestProvider(provider.clone())), Params {
|
|
network_id: 2,
|
|
config: Default::default(),
|
|
capabilities: capabilities,
|
|
sample_store: None,
|
|
});
|
|
|
|
(provider, proto)
|
|
}
|
|
|
|
fn status(chain_info: BlockChainInfo) -> Status {
|
|
Status {
|
|
protocol_version: 1,
|
|
network_id: 2,
|
|
head_td: chain_info.total_difficulty,
|
|
head_hash: chain_info.best_block_hash,
|
|
head_num: chain_info.best_block_number,
|
|
genesis_hash: chain_info.genesis_hash,
|
|
last_head: None,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn handshake_expected() {
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
|
|
let status = status(provider.client.chain_info());
|
|
|
|
let packet_body = write_handshake(&status, &capabilities, &proto);
|
|
|
|
proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body));
|
|
}
|
|
|
|
#[test]
|
|
fn reserved_handshake_expected() {
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
|
|
let status = status(provider.client.chain_info());
|
|
|
|
let packet_body = write_free_handshake(&status, &capabilities, &proto);
|
|
|
|
proto.on_connect(0xff, &Expect::Send(0xff, packet::STATUS, packet_body));
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn genesis_mismatch() {
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
|
|
let mut status = status(provider.client.chain_info());
|
|
status.genesis_hash = H256::zero();
|
|
|
|
let packet_body = write_handshake(&status, &capabilities, &proto);
|
|
|
|
proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body));
|
|
}
|
|
|
|
#[test]
|
|
fn credit_overflow() {
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
|
|
let status = status(provider.client.chain_info());
|
|
|
|
{
|
|
let packet_body = write_handshake(&status, &capabilities, &proto);
|
|
proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body));
|
|
}
|
|
|
|
{
|
|
let my_status = write_handshake(&status, &capabilities, &proto);
|
|
proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &my_status);
|
|
}
|
|
|
|
// 1 billion requests is far too many for the default flow params.
|
|
let requests = encode_single(Request::Headers(IncompleteHeadersRequest {
|
|
start: HashOrNumber::Number(1).into(),
|
|
max: 1_000_000_000,
|
|
skip: 0,
|
|
reverse: false,
|
|
}));
|
|
let request = make_packet(111, &requests);
|
|
|
|
proto.handle_packet(&Expect::Punish(1), 1, packet::REQUEST, &request);
|
|
}
|
|
|
|
// test the basic request types -- these just make sure that requests are parsed
|
|
// and sent to the provider correctly as well as testing response formatting.
|
|
|
|
#[test]
|
|
fn get_block_headers() {
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
let flow_params = proto.flow_params.read().clone();
|
|
|
|
let cur_status = status(provider.client.chain_info());
|
|
let my_status = write_handshake(&cur_status, &capabilities, &proto);
|
|
|
|
provider.client.add_blocks(100, EachBlockWith::Nothing);
|
|
|
|
let cur_status = status(provider.client.chain_info());
|
|
|
|
{
|
|
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
|
|
proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body));
|
|
proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &my_status);
|
|
}
|
|
|
|
let request = Request::Headers(IncompleteHeadersRequest {
|
|
start: HashOrNumber::Number(1).into(),
|
|
max: 10,
|
|
skip: 0,
|
|
reverse: false,
|
|
});
|
|
|
|
let req_id = 111;
|
|
|
|
let requests = encode_single(request.clone());
|
|
let request_body = make_packet(req_id, &requests);
|
|
|
|
let response = {
|
|
let headers: Vec<_> = (0..10).map(|i| provider.client.block_header(BlockId::Number(i + 1)).unwrap()).collect();
|
|
assert_eq!(headers.len(), 10);
|
|
|
|
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
|
|
|
let response = vec![Response::Headers(HeadersResponse { headers })];
|
|
|
|
let mut stream = RlpStream::new_list(3);
|
|
stream.append(&req_id).append(&new_creds).append_list(&response);
|
|
|
|
stream.out()
|
|
};
|
|
|
|
let expected = Expect::Respond(packet::RESPONSE, response);
|
|
proto.handle_packet(&expected, 1, packet::REQUEST, &request_body);
|
|
}
|
|
|
|
#[test]
|
|
fn get_block_bodies() {
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
let flow_params = proto.flow_params.read().clone();
|
|
|
|
let cur_status = status(provider.client.chain_info());
|
|
let my_status = write_handshake(&cur_status, &capabilities, &proto);
|
|
|
|
provider.client.add_blocks(100, EachBlockWith::Nothing);
|
|
|
|
let cur_status = status(provider.client.chain_info());
|
|
|
|
{
|
|
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
|
|
proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body));
|
|
proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &my_status);
|
|
}
|
|
|
|
let mut builder = Builder::default();
|
|
let mut bodies = Vec::new();
|
|
|
|
for i in 0..10 {
|
|
let hash = provider.client.block_header(BlockId::Number(i)).unwrap().hash();
|
|
builder.push(Request::Body(IncompleteBodyRequest {
|
|
hash: hash.into(),
|
|
})).unwrap();
|
|
bodies.push(Response::Body(provider.client.block_body(CompleteBodyRequest {
|
|
hash: hash,
|
|
}).unwrap()));
|
|
}
|
|
let req_id = 111;
|
|
let requests = builder.build();
|
|
let request_body = make_packet(req_id, &requests);
|
|
|
|
let response = {
|
|
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
|
|
|
let mut response_stream = RlpStream::new_list(3);
|
|
response_stream.append(&req_id).append(&new_creds).append_list(&bodies);
|
|
response_stream.out()
|
|
};
|
|
|
|
let expected = Expect::Respond(packet::RESPONSE, response);
|
|
proto.handle_packet(&expected, 1, packet::REQUEST, &request_body);
|
|
}
|
|
|
|
#[test]
|
|
fn get_block_receipts() {
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
let flow_params = proto.flow_params.read().clone();
|
|
|
|
let cur_status = status(provider.client.chain_info());
|
|
let my_status = write_handshake(&cur_status, &capabilities, &proto);
|
|
|
|
provider.client.add_blocks(1000, EachBlockWith::Nothing);
|
|
|
|
let cur_status = status(provider.client.chain_info());
|
|
|
|
{
|
|
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
|
|
proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body));
|
|
proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &my_status);
|
|
}
|
|
|
|
// find the first 10 block hashes starting with `f` because receipts are only provided
|
|
// by the test client in that case.
|
|
let block_hashes: Vec<H256> = (0..1000)
|
|
.map(|i| provider.client.block_header(BlockId::Number(i)).unwrap().hash())
|
|
.filter(|hash| format!("{}", hash).starts_with("0xf"))
|
|
.take(10)
|
|
.collect();
|
|
|
|
let mut builder = Builder::default();
|
|
let mut receipts = Vec::new();
|
|
for hash in block_hashes.iter().cloned() {
|
|
builder.push(Request::Receipts(IncompleteReceiptsRequest { hash: hash.into() })).unwrap();
|
|
receipts.push(Response::Receipts(provider.client.block_receipts(CompleteReceiptsRequest {
|
|
hash: hash
|
|
}).unwrap()));
|
|
}
|
|
|
|
let req_id = 111;
|
|
let requests = builder.build();
|
|
let request_body = make_packet(req_id, &requests);
|
|
|
|
let response = {
|
|
assert_eq!(receipts.len(), 10);
|
|
|
|
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
|
|
|
let mut response_stream = RlpStream::new_list(3);
|
|
response_stream.append(&req_id).append(&new_creds).append_list(&receipts);
|
|
response_stream.out()
|
|
};
|
|
|
|
let expected = Expect::Respond(packet::RESPONSE, response);
|
|
proto.handle_packet(&expected, 1, packet::REQUEST, &request_body);
|
|
}
|
|
|
|
#[test]
|
|
fn get_state_proofs() {
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
let flow_params = proto.flow_params.read().clone();
|
|
|
|
let provider = TestProvider(provider);
|
|
|
|
let cur_status = status(provider.0.client.chain_info());
|
|
|
|
{
|
|
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
|
|
proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body.clone()));
|
|
proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &packet_body);
|
|
}
|
|
|
|
let req_id = 112;
|
|
let key1: H256 = BigEndianHash::from_uint(&U256::from(11223344));
|
|
let key2: H256 = BigEndianHash::from_uint(&U256::from(99988887));
|
|
|
|
let mut builder = Builder::default();
|
|
builder.push(Request::Account(IncompleteAccountRequest {
|
|
block_hash: H256::zero().into(),
|
|
address_hash: key1.into(),
|
|
})).unwrap();
|
|
builder.push(Request::Storage(IncompleteStorageRequest {
|
|
block_hash: H256::zero().into(),
|
|
address_hash: key1.into(),
|
|
key_hash: key2.into(),
|
|
})).unwrap();
|
|
|
|
let requests = builder.build();
|
|
|
|
let request_body = make_packet(req_id, &requests);
|
|
let response = {
|
|
let responses = vec![
|
|
Response::Account(provider.account_proof(CompleteAccountRequest {
|
|
block_hash: H256::zero(),
|
|
address_hash: key1,
|
|
}).unwrap()),
|
|
Response::Storage(provider.storage_proof(CompleteStorageRequest {
|
|
block_hash: H256::zero(),
|
|
address_hash: key1,
|
|
key_hash: key2,
|
|
}).unwrap()),
|
|
];
|
|
|
|
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
|
|
|
let mut response_stream = RlpStream::new_list(3);
|
|
response_stream.append(&req_id).append(&new_creds).append_list(&responses);
|
|
response_stream.out()
|
|
};
|
|
|
|
let expected = Expect::Respond(packet::RESPONSE, response);
|
|
proto.handle_packet(&expected, 1, packet::REQUEST, &request_body);
|
|
}
|
|
|
|
#[test]
|
|
fn get_contract_code() {
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
let flow_params = proto.flow_params.read().clone();
|
|
|
|
let cur_status = status(provider.client.chain_info());
|
|
|
|
{
|
|
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
|
|
proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body.clone()));
|
|
proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &packet_body);
|
|
}
|
|
|
|
let req_id = 112;
|
|
let key1: H256 = BigEndianHash::from_uint(&U256::from(11223344));
|
|
let key2: H256 = BigEndianHash::from_uint(&U256::from(99988887));
|
|
|
|
let request = Request::Code(IncompleteCodeRequest {
|
|
block_hash: key1.into(),
|
|
code_hash: key2.into(),
|
|
});
|
|
|
|
let requests = encode_single(request.clone());
|
|
let request_body = make_packet(req_id, &requests);
|
|
let response = {
|
|
let response = vec![Response::Code(CodeResponse {
|
|
code: key1.as_bytes().iter().chain(key2.as_bytes().iter()).cloned().collect(),
|
|
})];
|
|
|
|
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
|
|
|
let mut response_stream = RlpStream::new_list(3);
|
|
|
|
response_stream.append(&req_id).append(&new_creds).append_list(&response);
|
|
response_stream.out()
|
|
};
|
|
|
|
let expected = Expect::Respond(packet::RESPONSE, response);
|
|
proto.handle_packet(&expected, 1, packet::REQUEST, &request_body);
|
|
}
|
|
|
|
#[test]
|
|
fn epoch_signal() {
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
let flow_params = proto.flow_params.read().clone();
|
|
|
|
let cur_status = status(provider.client.chain_info());
|
|
|
|
{
|
|
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
|
|
proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body.clone()));
|
|
proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &packet_body);
|
|
}
|
|
|
|
let req_id = 112;
|
|
let request = Request::Signal(request::IncompleteSignalRequest {
|
|
block_hash: H256([1; 32]).into(),
|
|
});
|
|
|
|
let requests = encode_single(request.clone());
|
|
let request_body = make_packet(req_id, &requests);
|
|
|
|
let response = {
|
|
let response = vec![Response::Signal(SignalResponse {
|
|
signal: vec![1, 2, 3, 4],
|
|
})];
|
|
|
|
let limit = *flow_params.limit();
|
|
let cost = flow_params.compute_cost_multi(requests.requests()).unwrap();
|
|
|
|
let new_creds = limit - cost;
|
|
|
|
let mut response_stream = RlpStream::new_list(3);
|
|
response_stream.append(&req_id).append(&new_creds).append_list(&response);
|
|
|
|
response_stream.out()
|
|
};
|
|
|
|
let expected = Expect::Respond(packet::RESPONSE, response);
|
|
proto.handle_packet(&expected, 1, packet::REQUEST, &request_body);
|
|
}
|
|
|
|
#[test]
|
|
fn proof_of_execution() {
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
let flow_params = proto.flow_params.read().clone();
|
|
|
|
let cur_status = status(provider.client.chain_info());
|
|
|
|
{
|
|
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
|
|
proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body.clone()));
|
|
proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &packet_body);
|
|
}
|
|
|
|
let req_id = 112;
|
|
let mut request = Request::Execution(request::IncompleteExecutionRequest {
|
|
block_hash: H256::zero().into(),
|
|
from: Address::zero(),
|
|
action: Action::Call(Address::zero()),
|
|
gas: 100.into(),
|
|
gas_price: 0.into(),
|
|
value: 0.into(),
|
|
data: Vec::new(),
|
|
});
|
|
|
|
// first: a valid amount to request execution of.
|
|
let requests = encode_single(request.clone());
|
|
let request_body = make_packet(req_id, &requests);
|
|
|
|
let response = {
|
|
let limit = *flow_params.limit();
|
|
let cost = flow_params.compute_cost_multi(requests.requests()).unwrap();
|
|
|
|
let new_creds = limit - cost;
|
|
|
|
let mut response_stream = RlpStream::new_list(3);
|
|
response_stream.append(&req_id).append(&new_creds).begin_list(0);
|
|
|
|
response_stream.out()
|
|
};
|
|
|
|
let expected = Expect::Respond(packet::RESPONSE, response);
|
|
proto.handle_packet(&expected, 1, packet::REQUEST, &request_body);
|
|
|
|
// next: way too much requested gas.
|
|
if let Request::Execution(ref mut req) = request {
|
|
req.gas = 100_000_000.into();
|
|
}
|
|
let req_id = 113;
|
|
let requests = encode_single(request.clone());
|
|
let request_body = make_packet(req_id, &requests);
|
|
|
|
let expected = Expect::Punish(1);
|
|
proto.handle_packet(&expected, 1, packet::REQUEST, &request_body);
|
|
}
|
|
|
|
#[test]
|
|
fn id_guard() {
|
|
use super::request_set::RequestSet;
|
|
use super::ReqId;
|
|
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
let flow_params = proto.flow_params.read().clone();
|
|
|
|
let req_id_1 = ReqId(5143);
|
|
let req_id_2 = ReqId(1111);
|
|
|
|
let req = encode_single(Request::Headers(IncompleteHeadersRequest {
|
|
start: HashOrNumber::Number(5u64).into(),
|
|
max: 100,
|
|
skip: 0,
|
|
reverse: false,
|
|
}));
|
|
|
|
let peer_id = 9876;
|
|
|
|
let mut pending_requests = RequestSet::default();
|
|
|
|
pending_requests.insert(req_id_1, req.clone(), 0.into(), Instant::now());
|
|
pending_requests.insert(req_id_2, req, 1.into(), Instant::now());
|
|
|
|
proto.peers.write().insert(peer_id, ::parking_lot::Mutex::new(Peer {
|
|
local_credits: flow_params.create_credits(),
|
|
status: status(provider.client.chain_info()),
|
|
capabilities,
|
|
remote_flow: Some((flow_params.create_credits(), (&*flow_params).clone())),
|
|
sent_head: provider.client.chain_info().best_block_hash,
|
|
last_update: Instant::now(),
|
|
pending_requests: pending_requests,
|
|
failed_requests: Vec::new(),
|
|
propagated_transactions: Default::default(),
|
|
skip_update: false,
|
|
local_flow: flow_params,
|
|
awaiting_acknowledge: None,
|
|
}));
|
|
|
|
// first, malformed responses.
|
|
{
|
|
let mut stream = RlpStream::new_list(3);
|
|
stream.append(&req_id_1.0);
|
|
stream.append(&4_000_000_usize);
|
|
stream.begin_list(2).append(&125_usize).append(&3_usize);
|
|
|
|
let packet = stream.out();
|
|
assert!(proto.response(peer_id, &Expect::Nothing, &Rlp::new(&packet)).is_err());
|
|
}
|
|
|
|
// next, do an unexpected response.
|
|
{
|
|
let mut stream = RlpStream::new_list(3);
|
|
stream.append(&10000_usize);
|
|
stream.append(&3_000_000_usize);
|
|
stream.begin_list(0);
|
|
|
|
let packet = stream.out();
|
|
assert!(proto.response(peer_id, &Expect::Nothing, &Rlp::new(&packet)).is_err());
|
|
}
|
|
|
|
// lastly, do a valid (but empty) response.
|
|
{
|
|
let mut stream = RlpStream::new_list(3);
|
|
stream.append(&req_id_2.0);
|
|
stream.append(&3_000_000_usize);
|
|
stream.begin_list(0);
|
|
|
|
let packet = stream.out();
|
|
assert!(proto.response(peer_id, &Expect::Nothing, &Rlp::new(&packet)).is_ok());
|
|
}
|
|
|
|
let peers = proto.peers.read();
|
|
if let Some(ref peer_info) = peers.get(&peer_id) {
|
|
let peer_info = peer_info.lock();
|
|
assert!(peer_info.pending_requests.collect_ids::<Vec<_>>().is_empty());
|
|
assert_eq!(peer_info.failed_requests, &[req_id_1]);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn get_transaction_index() {
|
|
let capabilities = capabilities();
|
|
|
|
let (provider, proto) = setup(capabilities);
|
|
let flow_params = proto.flow_params.read().clone();
|
|
|
|
let cur_status = status(provider.client.chain_info());
|
|
|
|
{
|
|
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
|
|
proto.on_connect(1, &Expect::Send(1, packet::STATUS, packet_body.clone()));
|
|
proto.handle_packet(&Expect::Nothing, 1, packet::STATUS, &packet_body);
|
|
}
|
|
|
|
let req_id = 112;
|
|
let key1: H256 = BigEndianHash::from_uint(&U256::from(11223344));
|
|
|
|
let request = Request::TransactionIndex(IncompleteTransactionIndexRequest {
|
|
hash: key1.into(),
|
|
});
|
|
|
|
let requests = encode_single(request.clone());
|
|
let request_body = make_packet(req_id, &requests);
|
|
let response = {
|
|
let response = vec![Response::TransactionIndex(TransactionIndexResponse {
|
|
num: 100,
|
|
hash: key1,
|
|
index: 55,
|
|
})];
|
|
|
|
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
|
|
|
let mut response_stream = RlpStream::new_list(3);
|
|
|
|
response_stream.append(&req_id).append(&new_creds).append_list(&response);
|
|
response_stream.out()
|
|
};
|
|
|
|
let expected = Expect::Respond(packet::RESPONSE, response);
|
|
proto.handle_packet(&expected, 1, packet::REQUEST, &request_body);
|
|
}
|
|
|
|
#[test]
|
|
fn sync_statistics() {
|
|
let mut stats = Statistics::new();
|
|
|
|
// Empty set should return 1.0
|
|
assert_eq!(stats.avg_peer_count(), 1.0);
|
|
|
|
// Average < 1.0 should return 1.0
|
|
stats.add_peer_count(0);
|
|
assert_eq!(stats.avg_peer_count(), 1.0);
|
|
|
|
stats = Statistics::new();
|
|
|
|
const N: f64 = 50.0;
|
|
|
|
for i in 1..(N as usize + 1) {
|
|
stats.add_peer_count(i);
|
|
}
|
|
|
|
// Compute the average for the sum 1..N
|
|
assert_eq!(stats.avg_peer_count(), N * (N + 1.0) / 2.0 / N);
|
|
|
|
for _ in 1..(MOVING_SAMPLE_SIZE + 1) {
|
|
stats.add_peer_count(40);
|
|
}
|
|
|
|
// Test that it returns the average of the last
|
|
// `MOVING_SAMPLE_SIZE` values
|
|
assert_eq!(stats.avg_peer_count(), 40.0);
|
|
}
|