mod.rs 9.99 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot 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.

// Polkadot 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 Polkadot.  If not, see <http://www.gnu.org/licenses/>.

17
//! Tests for polkadot and validation network.
18

19
use super::{PolkadotProtocol, Status, Message, FullStatus};
20
use validation::{ValidationSession, Knowledge};
21
22

use parking_lot::Mutex;
23
use polkadot_validation::GenericStatement;
24
use polkadot_primitives::{Block, SessionKey};
Gav Wood's avatar
Gav Wood committed
25
26
use polkadot_primitives::parachain::{CandidateReceipt, HeadData, BlockData, CollatorId, ValidatorId};
use substrate_primitives::crypto::UncheckedInto;
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
27
use codec::Encode;
28
29
30
31
32
use substrate_network::{
	Severity, NodeIndex, PeerInfo, ClientHandle, Context, config::Roles,
	message::Message as SubstrateMessage, specialization::NetworkSpecialization,
	generic_message::Message as GenericMessage
};
33
34
35
36

use std::sync::Arc;
use futures::Future;

37
mod validation;
38

39
40
#[derive(Default)]
struct TestContext {
Gav Wood's avatar
Gav Wood committed
41
42
43
	disabled: Vec<NodeIndex>,
	disconnected: Vec<NodeIndex>,
	messages: Vec<(NodeIndex, SubstrateMessage<Block>)>,
44
45
46
47
48
49
50
}

impl Context<Block> for TestContext {
	fn client(&self) -> &ClientHandle<Block> {
		unimplemented!()
	}

Gav Wood's avatar
Gav Wood committed
51
	fn report_peer(&mut self, peer: NodeIndex, reason: Severity) {
52
53
54
55
		match reason {
			Severity::Bad(_) => self.disabled.push(peer),
			_ => self.disconnected.push(peer),
		}
56
57
	}

Gav Wood's avatar
Gav Wood committed
58
	fn peer_info(&self, _peer: NodeIndex) -> Option<PeerInfo<Block>> {
59
60
61
		unimplemented!()
	}

Gav Wood's avatar
Gav Wood committed
62
63
	fn send_message(&mut self, who: NodeIndex, data: SubstrateMessage<Block>) {
		self.messages.push((who, data))
64
65
66
67
	}
}

impl TestContext {
Gav Wood's avatar
Gav Wood committed
68
	fn has_message(&self, to: NodeIndex, message: Message) -> bool {
69
70
		use substrate_network::generic_message::Message as GenericMessage;

71
		let encoded = message.encode();
72
73
74
75
76
77
78
		self.messages.iter().any(|&(ref peer, ref msg)| match msg {
			GenericMessage::ChainSpecific(ref data) => peer == &to && data == &encoded,
			_ => false,
		})
	}
}

79
fn make_status(status: &Status, roles: Roles) -> FullStatus {
80
81
	FullStatus {
		version: 1,
82
		min_supported_version: 1,
83
84
85
86
87
88
89
90
		roles,
		best_number: 0,
		best_hash: Default::default(),
		genesis_hash: Default::default(),
		chain_status: status.encode(),
	}
}

91
fn make_validation_session(local_key: SessionKey) -> (ValidationSession, Arc<Mutex<Knowledge>>) {
92
	let knowledge = Arc::new(Mutex::new(Knowledge::new()));
93
	let c = ValidationSession::new(knowledge.clone(), local_key);
94
95
96
97

	(c, knowledge)
}

Gav Wood's avatar
Gav Wood committed
98
fn on_message(protocol: &mut PolkadotProtocol, ctx: &mut TestContext, from: NodeIndex, message: Message) {
99
	let encoded = message.encode();
100
	protocol.on_message(ctx, from, &mut Some(GenericMessage::ChainSpecific(encoded)));
101
102
103
104
}

#[test]
fn sends_session_key() {
105
	let mut protocol = PolkadotProtocol::new(None);
106
107
108
109

	let peer_a = 1;
	let peer_b = 2;
	let parent_hash = [0; 32].into();
Gav Wood's avatar
Gav Wood committed
110
	let local_key: ValidatorId = [1; 32].unchecked_into();
111

112
	let validator_status = Status { collating_for: None };
Gav Wood's avatar
Gav Wood committed
113
	let collator_status = Status { collating_for: Some(([2; 32].unchecked_into(), 5.into())) };
114
115
116

	{
		let mut ctx = TestContext::default();
117
		protocol.on_connect(&mut ctx, peer_a, make_status(&validator_status, Roles::AUTHORITY));
118
119
120
121
122
		assert!(ctx.messages.is_empty());
	}

	{
		let mut ctx = TestContext::default();
Gav Wood's avatar
Gav Wood committed
123
		let (session, _knowledge) = make_validation_session(local_key.clone());
124
		protocol.new_validation_session(&mut ctx, parent_hash, session);
Gav Wood's avatar
Gav Wood committed
125
		assert!(ctx.has_message(peer_a, Message::SessionKey(local_key.clone())));
126
127
128
129
	}

	{
		let mut ctx = TestContext::default();
130
		protocol.on_connect(&mut ctx, peer_b, make_status(&collator_status, Roles::NONE));
131
		assert!(ctx.has_message(peer_b, Message::SessionKey(local_key)));
132
133
134
135
136
	}
}

#[test]
fn fetches_from_those_with_knowledge() {
137
	let mut protocol = PolkadotProtocol::new(None);
138
139
140
141

	let peer_a = 1;
	let peer_b = 2;
	let parent_hash = [0; 32].into();
Gav Wood's avatar
Gav Wood committed
142
	let local_key: ValidatorId = [1; 32].unchecked_into();
143
144
145
146
147

	let block_data = BlockData(vec![1, 2, 3, 4]);
	let block_data_hash = block_data.hash();
	let candidate_receipt = CandidateReceipt {
		parachain_index: 5.into(),
Gav Wood's avatar
Gav Wood committed
148
		collator: [255; 32].unchecked_into(),
149
		head_data: HeadData(vec![9, 9, 9]),
Gav Wood's avatar
Gav Wood committed
150
		signature: Default::default(),
151
152
153
154
155
156
157
		balance_uploads: Vec::new(),
		egress_queue_roots: Vec::new(),
		fees: 1_000_000,
		block_data_hash,
	};

	let candidate_hash = candidate_receipt.hash();
Gav Wood's avatar
Gav Wood committed
158
159
	let a_key: ValidatorId = [3; 32].unchecked_into();
	let b_key: ValidatorId = [4; 32].unchecked_into();
160
161
162

	let status = Status { collating_for: None };

Gav Wood's avatar
Gav Wood committed
163
	let (session, knowledge) = make_validation_session(local_key.clone());
164
	protocol.new_validation_session(&mut TestContext::default(), parent_hash, session);
165

Gav Wood's avatar
Gav Wood committed
166
	knowledge.lock().note_statement(a_key.clone(), &GenericStatement::Valid(candidate_hash));
167
168
169
170
171
	let recv = protocol.fetch_block_data(&mut TestContext::default(), &candidate_receipt, parent_hash);

	// connect peer A
	{
		let mut ctx = TestContext::default();
172
		protocol.on_connect(&mut ctx, peer_a, make_status(&status, Roles::AUTHORITY));
173
		assert!(ctx.has_message(peer_a, Message::SessionKey(local_key)));
174
175
176
177
178
	}

	// peer A gives session key and gets asked for data.
	{
		let mut ctx = TestContext::default();
Gav Wood's avatar
Gav Wood committed
179
		on_message(&mut protocol, &mut ctx, peer_a, Message::SessionKey(a_key.clone()));
180
		assert!(protocol.validators.contains_key(&a_key));
181
		assert!(ctx.has_message(peer_a, Message::RequestBlockData(1, parent_hash, candidate_hash)));
182
183
	}

Gav Wood's avatar
Gav Wood committed
184
	knowledge.lock().note_statement(b_key.clone(), &GenericStatement::Valid(candidate_hash));
185
186
187
188

	// peer B connects and sends session key. request already assigned to A
	{
		let mut ctx = TestContext::default();
189
		protocol.on_connect(&mut ctx, peer_b, make_status(&status, Roles::AUTHORITY));
190
		on_message(&mut protocol, &mut ctx, peer_b, Message::SessionKey(b_key));
191
		assert!(!ctx.has_message(peer_b, Message::RequestBlockData(2, parent_hash, candidate_hash)));
192
193
194
195
196
197
198

	}

	// peer A disconnects, triggering reassignment
	{
		let mut ctx = TestContext::default();
		protocol.on_disconnect(&mut ctx, peer_a);
199
		assert!(!protocol.validators.contains_key(&a_key));
200
		assert!(ctx.has_message(peer_b, Message::RequestBlockData(2, parent_hash, candidate_hash)));
201
202
203
204
205
206
207
208
209
210
	}

	// peer B comes back with block data.
	{
		let mut ctx = TestContext::default();
		on_message(&mut protocol, &mut ctx, peer_b, Message::BlockData(2, Some(block_data.clone())));
		drop(protocol);
		assert_eq!(recv.wait().unwrap(), block_data);
	}
}
211

212
213
214
215
216
217
218
219
220
221
222
223
#[test]
fn fetches_available_block_data() {
	let mut protocol = PolkadotProtocol::new(None);

	let peer_a = 1;
	let parent_hash = [0; 32].into();

	let block_data = BlockData(vec![1, 2, 3, 4]);
	let block_data_hash = block_data.hash();
	let para_id = 5.into();
	let candidate_receipt = CandidateReceipt {
		parachain_index: para_id,
Gav Wood's avatar
Gav Wood committed
224
		collator: [255; 32].unchecked_into(),
225
		head_data: HeadData(vec![9, 9, 9]),
Gav Wood's avatar
Gav Wood committed
226
		signature: Default::default(),
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
		balance_uploads: Vec::new(),
		egress_queue_roots: Vec::new(),
		fees: 1_000_000,
		block_data_hash,
	};

	let candidate_hash = candidate_receipt.hash();
	let av_store = ::av_store::Store::new_in_memory();

	let status = Status { collating_for: None };

	protocol.register_availability_store(av_store.clone());

	av_store.make_available(::av_store::Data {
		relay_parent: parent_hash,
		parachain_id: para_id,
		candidate_hash,
		block_data: block_data.clone(),
		extrinsic: None,
	}).unwrap();

	// connect peer A
	{
		let mut ctx = TestContext::default();
		protocol.on_connect(&mut ctx, peer_a, make_status(&status, Roles::FULL));
	}

	// peer A asks for historic block data and gets response
	{
		let mut ctx = TestContext::default();
		on_message(&mut protocol, &mut ctx, peer_a, Message::RequestBlockData(1, parent_hash, candidate_hash));
		assert!(ctx.has_message(peer_a, Message::BlockData(1, Some(block_data))));
	}
}

262
263
#[test]
fn remove_bad_collator() {
264
	let mut protocol = PolkadotProtocol::new(None);
265

Gav Wood's avatar
Gav Wood committed
266
	let who = 1;
Gav Wood's avatar
Gav Wood committed
267
	let collator_id: CollatorId = [2; 32].unchecked_into();
268

Gav Wood's avatar
Gav Wood committed
269
	let status = Status { collating_for: Some((collator_id.clone(), 5.into())) };
270
271
272

	{
		let mut ctx = TestContext::default();
Gav Wood's avatar
Gav Wood committed
273
		protocol.on_connect(&mut ctx, who, make_status(&status, Roles::NONE));
274
275
276
277
	}

	{
		let mut ctx = TestContext::default();
Gav Wood's avatar
Gav Wood committed
278
		protocol.disconnect_bad_collator(&mut ctx, collator_id);
Gav Wood's avatar
Gav Wood committed
279
		assert!(ctx.disabled.contains(&who));
280
281
	}
}
282
283
284
285
286
287
288
289

#[test]
fn many_session_keys() {
	let mut protocol = PolkadotProtocol::new(None);

	let parent_a = [1; 32].into();
	let parent_b = [2; 32].into();

Gav Wood's avatar
Gav Wood committed
290
291
	let local_key_a: ValidatorId = [3; 32].unchecked_into();
	let local_key_b: ValidatorId = [4; 32].unchecked_into();
292

Gav Wood's avatar
Gav Wood committed
293
294
	let (session_a, _knowledge_a) = make_validation_session(local_key_a.clone());
	let (session_b, _knowledge_b) = make_validation_session(local_key_b.clone());
295

296
297
	protocol.new_validation_session(&mut TestContext::default(), parent_a, session_a);
	protocol.new_validation_session(&mut TestContext::default(), parent_b, session_b);
298

Gav Wood's avatar
Gav Wood committed
299
	assert_eq!(protocol.live_validation_sessions.recent_keys(), &[local_key_a.clone(), local_key_b.clone()]);
300
301
302
303
304
305
306
307
308
309

	let peer_a = 1;

	// when connecting a peer, we should get both those keys.
	{
		let mut ctx = TestContext::default();

		let status = Status { collating_for: None };
		protocol.on_connect(&mut ctx, peer_a, make_status(&status, Roles::AUTHORITY));

Gav Wood's avatar
Gav Wood committed
310
311
		assert!(ctx.has_message(peer_a, Message::SessionKey(local_key_a.clone())));
		assert!(ctx.has_message(peer_a, Message::SessionKey(local_key_b.clone())));
312
313
314
315
	}

	let peer_b = 2;

316
	protocol.remove_validation_session(&parent_a);
317
318
319
320
321
322
323
324
325
326
327

	{
		let mut ctx = TestContext::default();

		let status = Status { collating_for: None };
		protocol.on_connect(&mut ctx, peer_b, make_status(&status, Roles::AUTHORITY));

		assert!(!ctx.has_message(peer_b, Message::SessionKey(local_key_a)));
		assert!(ctx.has_message(peer_b, Message::SessionKey(local_key_b)));
	}
}