mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-10 19:17:22 +00:00
Availability/Extrinsic store (#465)
This commit is contained in:
committed by
Benjamin Kampmann
parent
a7c7bd49d9
commit
7143e85f39
@@ -26,6 +26,7 @@ extern crate substrate_network;
|
||||
extern crate substrate_primitives;
|
||||
|
||||
extern crate polkadot_api;
|
||||
extern crate polkadot_availability_store as av_store;
|
||||
extern crate polkadot_consensus;
|
||||
extern crate polkadot_primitives;
|
||||
|
||||
@@ -197,7 +198,9 @@ struct CurrentConsensus {
|
||||
|
||||
impl CurrentConsensus {
|
||||
// get locally stored block data for a candidate.
|
||||
fn block_data(&self, hash: &Hash) -> Option<BlockData> {
|
||||
fn block_data(&self, relay_parent: &Hash, hash: &Hash) -> Option<BlockData> {
|
||||
if relay_parent != &self.parent_hash { return None }
|
||||
|
||||
self.knowledge.lock().candidates.get(hash)
|
||||
.and_then(|entry| entry.block_data.clone())
|
||||
}
|
||||
@@ -211,8 +214,8 @@ pub enum Message {
|
||||
/// As a validator, tell the peer your current session key.
|
||||
// TODO: do this with a cryptographic proof of some kind
|
||||
SessionKey(SessionKey),
|
||||
/// Requesting parachain block data by candidate hash.
|
||||
RequestBlockData(RequestId, Hash),
|
||||
/// Requesting parachain block data by (relay_parent, candidate_hash).
|
||||
RequestBlockData(RequestId, Hash, Hash),
|
||||
/// Provide block data by candidate hash or nothing if unknown.
|
||||
BlockData(RequestId, Option<BlockData>),
|
||||
/// Tell a collator their role.
|
||||
@@ -233,9 +236,10 @@ impl Encode for Message {
|
||||
dest.push_byte(1);
|
||||
dest.push(k);
|
||||
}
|
||||
Message::RequestBlockData(ref id, ref d) => {
|
||||
Message::RequestBlockData(ref id, ref r, ref d) => {
|
||||
dest.push_byte(2);
|
||||
dest.push(id);
|
||||
dest.push(r);
|
||||
dest.push(d);
|
||||
}
|
||||
Message::BlockData(ref id, ref d) => {
|
||||
@@ -261,7 +265,10 @@ impl Decode for Message {
|
||||
match input.read_byte()? {
|
||||
0 => Some(Message::Statement(Decode::decode(input)?, Decode::decode(input)?)),
|
||||
1 => Some(Message::SessionKey(Decode::decode(input)?)),
|
||||
2 => Some(Message::RequestBlockData(Decode::decode(input)?, Decode::decode(input)?)),
|
||||
2 => {
|
||||
let x: (_, _, _) = Decode::decode(input)?;
|
||||
Some(Message::RequestBlockData(x.0, x.1, x.2))
|
||||
}
|
||||
3 => Some(Message::BlockData(Decode::decode(input)?, Decode::decode(input)?)),
|
||||
4 => Some(Message::CollatorRole(Decode::decode(input)?)),
|
||||
5 => Some(Message::Collation(Decode::decode(input)?, Decode::decode(input)?)),
|
||||
@@ -287,6 +294,7 @@ pub struct PolkadotProtocol {
|
||||
live_consensus: Option<CurrentConsensus>,
|
||||
in_flight: HashMap<(RequestId, NodeIndex), BlockDataRequest>,
|
||||
pending: Vec<BlockDataRequest>,
|
||||
extrinsic_store: Option<::av_store::Store>,
|
||||
next_req_id: u64,
|
||||
}
|
||||
|
||||
@@ -303,6 +311,7 @@ impl PolkadotProtocol {
|
||||
live_consensus: None,
|
||||
in_flight: HashMap::new(),
|
||||
pending: Vec::new(),
|
||||
extrinsic_store: None,
|
||||
next_req_id: 1,
|
||||
}
|
||||
}
|
||||
@@ -385,7 +394,7 @@ impl PolkadotProtocol {
|
||||
send_polkadot_message(
|
||||
ctx,
|
||||
who,
|
||||
Message::RequestBlockData(req_id, pending.candidate_hash)
|
||||
Message::RequestBlockData(req_id, pending.consensus_parent, pending.candidate_hash)
|
||||
);
|
||||
|
||||
self.in_flight.insert((req_id, who), pending);
|
||||
@@ -406,9 +415,12 @@ impl PolkadotProtocol {
|
||||
Message::Statement(parent_hash, _statement) =>
|
||||
self.consensus_gossip.on_chain_specific(ctx, who, raw, parent_hash),
|
||||
Message::SessionKey(key) => self.on_session_key(ctx, who, key),
|
||||
Message::RequestBlockData(req_id, hash) => {
|
||||
Message::RequestBlockData(req_id, relay_parent, candidate_hash) => {
|
||||
let block_data = self.live_consensus.as_ref()
|
||||
.and_then(|c| c.block_data(&hash));
|
||||
.and_then(|c| c.block_data(&relay_parent, &candidate_hash))
|
||||
.or_else(|| self.extrinsic_store.as_ref()
|
||||
.and_then(|s| s.block_data(relay_parent, candidate_hash))
|
||||
);
|
||||
|
||||
send_polkadot_message(ctx, who, Message::BlockData(req_id, block_data));
|
||||
}
|
||||
@@ -720,4 +732,9 @@ impl PolkadotProtocol {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// register availability store.
|
||||
pub fn register_availability_store(&mut self, extrinsic_store: ::av_store::Store) {
|
||||
self.extrinsic_store = Some(extrinsic_store);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ use tokio::runtime::TaskExecutor;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{NetworkService, Knowledge};
|
||||
@@ -135,8 +136,8 @@ impl<P: LocalPolkadotApi + Send + Sync + 'static> Router<P> {
|
||||
}
|
||||
|
||||
fn dispatch_work<D, E>(&self, candidate_hash: Hash, producer: StatementProducer<D, E>) where
|
||||
D: Future<Item=BlockData,Error=()> + Send + 'static,
|
||||
E: Future<Item=Extrinsic,Error=()> + Send + 'static,
|
||||
D: Future<Item=BlockData,Error=io::Error> + Send + 'static,
|
||||
E: Future<Item=Extrinsic,Error=io::Error> + Send + 'static,
|
||||
{
|
||||
let parent_hash = self.parent_hash.clone();
|
||||
|
||||
@@ -156,28 +157,34 @@ impl<P: LocalPolkadotApi + Send + Sync + 'static> Router<P> {
|
||||
let network = self.network.clone();
|
||||
let knowledge = self.knowledge.clone();
|
||||
|
||||
let work = producer.prime(validate).map(move |produced| {
|
||||
// store the data before broadcasting statements, so other peers can fetch.
|
||||
knowledge.lock().note_candidate(candidate_hash, produced.block_data, produced.extrinsic);
|
||||
let work = producer.prime(validate)
|
||||
.map(move |produced| {
|
||||
// store the data before broadcasting statements, so other peers can fetch.
|
||||
knowledge.lock().note_candidate(
|
||||
candidate_hash,
|
||||
produced.block_data,
|
||||
produced.extrinsic
|
||||
);
|
||||
|
||||
// propagate the statements
|
||||
if let Some(validity) = produced.validity {
|
||||
let signed = table.sign_and_import(validity.clone()).0;
|
||||
network.with_spec(|spec, ctx| spec.gossip_statement(ctx, parent_hash, signed));
|
||||
}
|
||||
// propagate the statements
|
||||
if let Some(validity) = produced.validity {
|
||||
let signed = table.sign_and_import(validity.clone()).0;
|
||||
network.with_spec(|spec, ctx| spec.gossip_statement(ctx, parent_hash, signed));
|
||||
}
|
||||
|
||||
if let Some(availability) = produced.availability {
|
||||
let signed = table.sign_and_import(availability).0;
|
||||
network.with_spec(|spec, ctx| spec.gossip_statement(ctx, parent_hash, signed));
|
||||
}
|
||||
});
|
||||
if let Some(availability) = produced.availability {
|
||||
let signed = table.sign_and_import(availability).0;
|
||||
network.with_spec(|spec, ctx| spec.gossip_statement(ctx, parent_hash, signed));
|
||||
}
|
||||
})
|
||||
.map_err(|e| debug!(target: "p_net", "Failed to produce statements: {:?}", e));
|
||||
|
||||
self.task_executor.spawn(work);
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: LocalPolkadotApi + Send> TableRouter for Router<P> {
|
||||
type Error = ();
|
||||
type Error = io::Error;
|
||||
type FetchCandidate = BlockDataReceiver;
|
||||
type FetchExtrinsic = Result<Extrinsic, Self::Error>;
|
||||
|
||||
@@ -213,12 +220,18 @@ pub struct BlockDataReceiver {
|
||||
|
||||
impl Future for BlockDataReceiver {
|
||||
type Item = BlockData;
|
||||
type Error = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<BlockData, ()> {
|
||||
fn poll(&mut self) -> Poll<BlockData, io::Error> {
|
||||
match self.inner {
|
||||
Some(ref mut inner) => inner.poll().map_err(|_| ()),
|
||||
None => return Err(()),
|
||||
Some(ref mut inner) => inner.poll().map_err(|_| io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Sending end of channel hung up",
|
||||
)),
|
||||
None => return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Network service is unavailable",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ fn fetches_from_those_with_knowledge() {
|
||||
let mut ctx = TestContext::default();
|
||||
on_message(&mut protocol, &mut ctx, peer_a, Message::SessionKey(a_key));
|
||||
assert!(protocol.validators.contains_key(&a_key));
|
||||
assert!(ctx.has_message(peer_a, Message::RequestBlockData(1, candidate_hash)));
|
||||
assert!(ctx.has_message(peer_a, Message::RequestBlockData(1, parent_hash, candidate_hash)));
|
||||
}
|
||||
|
||||
knowledge.lock().note_statement(b_key, &GenericStatement::Valid(candidate_hash));
|
||||
@@ -184,7 +184,7 @@ fn fetches_from_those_with_knowledge() {
|
||||
let mut ctx = TestContext::default();
|
||||
protocol.on_connect(&mut ctx, peer_b, make_status(&status, Roles::AUTHORITY));
|
||||
on_message(&mut protocol, &mut ctx, peer_b, Message::SessionKey(b_key));
|
||||
assert!(!ctx.has_message(peer_b, Message::RequestBlockData(2, candidate_hash)));
|
||||
assert!(!ctx.has_message(peer_b, Message::RequestBlockData(2, parent_hash, candidate_hash)));
|
||||
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ fn fetches_from_those_with_knowledge() {
|
||||
let mut ctx = TestContext::default();
|
||||
protocol.on_disconnect(&mut ctx, peer_a);
|
||||
assert!(!protocol.validators.contains_key(&a_key));
|
||||
assert!(ctx.has_message(peer_b, Message::RequestBlockData(2, candidate_hash)));
|
||||
assert!(ctx.has_message(peer_b, Message::RequestBlockData(2, parent_hash, candidate_hash)));
|
||||
}
|
||||
|
||||
// peer B comes back with block data.
|
||||
@@ -205,6 +205,56 @@ fn fetches_from_those_with_knowledge() {
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
collator: [255; 32].into(),
|
||||
head_data: HeadData(vec![9, 9, 9]),
|
||||
signature: H512::from([1; 64]).into(),
|
||||
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))));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_bad_collator() {
|
||||
let mut protocol = PolkadotProtocol::new(None);
|
||||
|
||||
Reference in New Issue
Block a user