Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

implement custom proposer #1320

Merged
merged 65 commits into from
Jul 5, 2020
Merged
Show file tree
Hide file tree
Changes from 64 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
54873ea
network bridge skeleton
rphmeier Jun 17, 2020
5eea3bf
move some primitives around and add debug impls
rphmeier Jun 17, 2020
27456b5
protocol registration glue & abstract network interface
rphmeier Jun 17, 2020
3bf17fa
add send_msgs to subsystemctx
rphmeier Jun 18, 2020
e05abd2
select logic
rphmeier Jun 18, 2020
ba005c9
transform different events into actions and handle
rphmeier Jun 18, 2020
7572bba
implement remaining network bridge state machine
rphmeier Jun 18, 2020
24a91c4
start test skeleton
rphmeier Jun 22, 2020
064a00c
make network methods asynchronous
rphmeier Jun 22, 2020
c116061
extract subsystem out to subsystem crate
rphmeier Jun 24, 2020
e6162de
port over overseer to subsystem context trait
rphmeier Jun 24, 2020
844a9d1
fix minimal example
rphmeier Jun 24, 2020
cae1561
fix overseer doc test
rphmeier Jun 24, 2020
2c9be73
update network-bridge crate
rphmeier Jun 24, 2020
534535f
write a subsystem test-helpers crate
rphmeier Jun 25, 2020
8ac6269
write a network test helper for network-bridge
rphmeier Jun 25, 2020
51ff607
set up (broken) view test
rphmeier Jun 25, 2020
960029b
Revamp network to be more async-friendly and not require Sync
rphmeier Jun 25, 2020
eb52f9a
fix spacing
rphmeier Jun 25, 2020
f6526c4
fix test compilation
rphmeier Jun 25, 2020
0f5a1b1
insert side-channel for actions
rphmeier Jun 25, 2020
8f75746
Add some more message types to AllMessages
rphmeier Jun 25, 2020
940216b
introduce a test harness
rphmeier Jun 25, 2020
a20cfd5
impl ProvideInherent for InclusionInherent
coriolinus Jun 26, 2020
5c44804
reduce import churn; correct expect message
coriolinus Jun 26, 2020
6efd05a
move inclusion inherent identifier into primitives
coriolinus Jun 26, 2020
fedd931
bump kusama spec_version to placate CI
coriolinus Jun 26, 2020
d696e1b
copy sc_basic_authorship::{ProposerFactory, Proposer}
coriolinus Jun 26, 2020
9f05198
use polkadot custom proposer instead of basic-authorship one
coriolinus Jun 26, 2020
859bd8b
add some tests
rphmeier Jun 26, 2020
c504d12
Merge branch 'master' into rh-network-bridge
rphmeier Jun 26, 2020
4343f5c
ensure service compiles and passes tests
rphmeier Jun 26, 2020
98c4c54
fix typo
rphmeier Jun 26, 2020
d194838
fix service-new compilation
rphmeier Jun 26, 2020
d468a2b
Subsystem test helpers send messages synchronously
rphmeier Jun 27, 2020
b676adc
remove smelly action inspector
rphmeier Jun 27, 2020
7d44a62
remove superfluous let binding
rphmeier Jun 27, 2020
989e8b5
fix warnings
rphmeier Jun 27, 2020
dffb0ab
Merge remote-tracking branch 'origin/master' into prgn-custom-proposer
coriolinus Jun 29, 2020
da07b9e
add license header
coriolinus Jun 29, 2020
3110bf8
empty commit; maybe github will notice the one with changes
coriolinus Jun 29, 2020
64792f7
Update node/network/bridge/src/lib.rs
rphmeier Jun 29, 2020
6b88341
add sanity check to only include valid inherents
coriolinus Jun 30, 2020
2b6a7d0
stub: encapsulate block production mechanics instead of copying them
coriolinus Jun 30, 2020
01f2cd9
Merge remote-tracking branch 'origin/prgn-inclusioninherent-providein…
coriolinus Jun 30, 2020
d2af626
partial implementation of propose fn
coriolinus Jun 30, 2020
50b51dd
fix compilation
rphmeier Jun 30, 2020
fd1dea5
Merge remote-tracking branch 'origin/rh-network-bridge' into prgn-cus…
coriolinus Jun 30, 2020
2ab09a7
Merge remote-tracking branch 'origin/rh-network-bridge' into prgn-cus…
coriolinus Jun 30, 2020
ad5cbc8
clear a few more compile errors
coriolinus Jun 30, 2020
4577215
Merge remote-tracking branch 'origin/master' into prgn-custom-proposer
coriolinus Jul 1, 2020
5bcf147
finish fn propose
coriolinus Jul 1, 2020
2377744
broken: add timeout to proposal
coriolinus Jul 1, 2020
af699dd
add timeout to proposal
coriolinus Jul 1, 2020
3642dce
guide: provisioner is responsible for selecting parachain candidates
coriolinus Jul 1, 2020
75190d8
implement ProvisionerMessage::RequestInherentData & update fn propose
coriolinus Jul 1, 2020
3063303
Merge remote-tracking branch 'origin/master' into prgn-custom-proposer
coriolinus Jul 2, 2020
6e41882
impl CreateProposer::init; clean up
coriolinus Jul 2, 2020
cfec8c2
impl std::error::Error for Error
coriolinus Jul 2, 2020
6092ba0
document error-handling rationale
coriolinus Jul 2, 2020
c78dd28
cause polkadot-service-new to compile correctly
coriolinus Jul 2, 2020
d37e89e
Move potentially-blocking call from fn init -> fn propose
coriolinus Jul 3, 2020
b91be19
document struct Proposer
coriolinus Jul 3, 2020
9bae56e
extract provisioner data fetch
coriolinus Jul 3, 2020
0b0b86c
Merge branch 'master' into prgn-custom-proposer
gavofyork Jul 5, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ members = [
"service",
"validation",

"node/core/proposer",
"node/network/bridge",
"node/overseer",
"node/primitives",
Expand Down
27 changes: 27 additions & 0 deletions node/core/proposer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "polkadot-node-core-proposer"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"

[dependencies]
futures = "0.3.4"
futures-timer = "3.0.1"
log = "0.4.8"
parity-scale-codec = "1.3.0"
polkadot-node-subsystem = { path = "../../subsystem" }
polkadot-overseer = { path = "../../overseer" }
polkadot-primitives = { path = "../../../primitives" }
sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-telemetry = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-blockchain = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" }
tokio-executor = { version = "0.2.0-alpha.6", features = ["blocking"] }
wasm-timer = "0.2.4"
262 changes: 262 additions & 0 deletions node/core/proposer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
use futures::prelude::*;
use futures::select;
use polkadot_node_subsystem::{messages::{AllMessages, ProvisionerInherentData, ProvisionerMessage}, SubsystemError};
use polkadot_overseer::OverseerHandler;
use polkadot_primitives::{
inclusion_inherent,
parachain::ParachainHost,
Block, Hash, Header,
};
use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider};
use sp_api::{ApiExt, ProvideRuntimeApi};
use sp_blockchain::HeaderBackend;
use sp_consensus::{Proposal, RecordProof};
use sp_inherents::InherentData;
use sp_runtime::traits::{DigestFor, HashFor};
use sp_transaction_pool::TransactionPool;
use std::{fmt, pin::Pin, sync::Arc, time};

/// How long proposal can take before we give up and err out
const PROPOSE_TIMEOUT: core::time::Duration = core::time::Duration::from_secs(2);

/// Custom Proposer factory for Polkadot
pub struct ProposerFactory<TxPool, Backend, Client> {
inner: sc_basic_authorship::ProposerFactory<TxPool, Backend, Client>,
overseer: OverseerHandler,
}

impl<TxPool, Backend, Client> ProposerFactory<TxPool, Backend, Client> {
pub fn new(
client: Arc<Client>,
transaction_pool: Arc<TxPool>,
overseer: OverseerHandler,
) -> Self {
ProposerFactory {
inner: sc_basic_authorship::ProposerFactory::new(
client,
transaction_pool,
None,
),
overseer,
}
}
}

impl<TxPool, Backend, Client> sp_consensus::Environment<Block>
for ProposerFactory<TxPool, Backend, Client>
where
TxPool: 'static + TransactionPool<Block = Block>,
Client: 'static
+ BlockBuilderProvider<Backend, Block, Client>
+ ProvideRuntimeApi<Block>
+ HeaderBackend<Block>
+ Send
+ Sync,
Client::Api:
ParachainHost<Block> + BlockBuilderApi<Block> + ApiExt<Block, Error = sp_blockchain::Error>,
Backend:
'static + sc_client_api::Backend<Block, State = sp_api::StateBackendFor<Client, Block>>,
// Rust bug: https://github.com/rust-lang/rust/issues/24159
sp_api::StateBackendFor<Client, Block>: sp_api::StateBackend<HashFor<Block>> + Send,
{
type CreateProposer = Pin<Box<
dyn Future<Output = Result<Self::Proposer, Self::Error>> + Send + 'static,
>>;
type Proposer = Proposer<TxPool, Backend, Client>;
type Error = Error;

fn init(&mut self, parent_header: &Header) -> Self::CreateProposer {
// create the inner proposer
let proposer = self.inner.init(parent_header).into_inner();

// data to be moved into the future
let overseer = self.overseer.clone();
let parent_header_hash = parent_header.hash();

async move {
Ok(Proposer {
inner: proposer?,
overseer,
parent_header_hash,
})
}.boxed()
}
}

/// Custom Proposer for Polkadot.
///
/// This proposer gets the ProvisionerInherentData and injects it into the wrapped
/// proposer's inherent data, then delegates the actual proposal generation.
pub struct Proposer<TxPool: TransactionPool<Block = Block>, Backend, Client> {
coriolinus marked this conversation as resolved.
Show resolved Hide resolved
inner: sc_basic_authorship::Proposer<Backend, Block, Client, TxPool>,
overseer: OverseerHandler,
parent_header_hash: Hash,
}

// This impl has the same generic bounds as the Proposer impl.
impl<TxPool, Backend, Client> Proposer<TxPool, Backend, Client>
where
TxPool: 'static + TransactionPool<Block = Block>,
Client: 'static
+ BlockBuilderProvider<Backend, Block, Client>
+ ProvideRuntimeApi<Block>
+ HeaderBackend<Block>
+ Send
+ Sync,
Client::Api:
ParachainHost<Block> + BlockBuilderApi<Block> + ApiExt<Block, Error = sp_blockchain::Error>,
Backend:
'static + sc_client_api::Backend<Block, State = sp_api::StateBackendFor<Client, Block>>,
// Rust bug: https://github.com/rust-lang/rust/issues/24159
sp_api::StateBackendFor<Client, Block>: sp_api::StateBackend<HashFor<Block>> + Send,
{
/// Get provisioner inherent data
///
/// This function has a constant timeout: `PROPOSE_TIMEOUT`.
fn get_provisioner_data(&self) -> impl Future<Output = Result<ProvisionerInherentData, Error>> {
// clone this (lightweight) data because we're going to move it into the future
let mut overseer = self.overseer.clone();
let parent_header_hash = self.parent_header_hash.clone();

let mut provisioner_inherent_data = async move {
let (sender, receiver) = futures::channel::oneshot::channel();

// strictly speaking, we don't _have_ to .await this send_msg before opening the
// receiver; it's possible that the response there would be ready slightly before
// this call completes. IMO it's not worth the hassle or overhead of spawning a
// distinct task for that kind of miniscule efficiency improvement.
overseer.send_msg(AllMessages::Provisioner(
ProvisionerMessage::RequestInherentData(parent_header_hash, sender),
)).await?;

receiver.await.map_err(Error::ClosedChannelFromProvisioner)
}
.boxed()
.fuse();

let mut timeout = wasm_timer::Delay::new(PROPOSE_TIMEOUT).fuse();

async move {
select! {
pid = provisioner_inherent_data => pid,
_ = timeout => Err(Error::Timeout),
}
}
}
}

impl<TxPool, Backend, Client> sp_consensus::Proposer<Block> for Proposer<TxPool, Backend, Client>
where
TxPool: 'static + TransactionPool<Block = Block>,
Client: 'static
+ BlockBuilderProvider<Backend, Block, Client>
+ ProvideRuntimeApi<Block>
+ HeaderBackend<Block>
+ Send
+ Sync,
Client::Api:
ParachainHost<Block> + BlockBuilderApi<Block> + ApiExt<Block, Error = sp_blockchain::Error>,
Backend:
'static + sc_client_api::Backend<Block, State = sp_api::StateBackendFor<Client, Block>>,
// Rust bug: https://github.com/rust-lang/rust/issues/24159
sp_api::StateBackendFor<Client, Block>: sp_api::StateBackend<HashFor<Block>> + Send,
{
type Transaction = sc_client_api::TransactionFor<Backend, Block>;
type Proposal = Pin<Box<
dyn Future<Output = Result<Proposal<Block, sp_api::TransactionFor<Client, Block>>, Error>> + Send,
>>;
type Error = Error;

fn propose(
self,
mut inherent_data: InherentData,
inherent_digests: DigestFor<Block>,
max_duration: time::Duration,
record_proof: RecordProof,
) -> Self::Proposal {
let provisioner_data = self.get_provisioner_data();

async move {
let provisioner_data = match provisioner_data.await {
Ok(pd) => pd,
Err(err) => {
log::warn!("could not get provisioner inherent data; injecting default data: {}", err);
Default::default()
}
};

inherent_data.put_data(
inclusion_inherent::INHERENT_IDENTIFIER,
&provisioner_data,
)?;

self.inner
.propose(inherent_data, inherent_digests, max_duration, record_proof)
.await
.map_err(Into::into)
}
.boxed()
}
}

// It would have been more ergonomic to use thiserror to derive the
// From implementations, Display, and std::error::Error, but unfortunately
// two of the wrapped errors (sp_inherents::Error, SubsystemError) also
// don't impl std::error::Error, which breaks the thiserror derive.
#[derive(Debug)]
pub enum Error {
coriolinus marked this conversation as resolved.
Show resolved Hide resolved
Consensus(sp_consensus::Error),
Blockchain(sp_blockchain::Error),
Inherent(sp_inherents::Error),
Timeout,
ClosedChannelFromProvisioner(futures::channel::oneshot::Canceled),
Subsystem(SubsystemError)
}

impl From<sp_consensus::Error> for Error {
fn from(e: sp_consensus::Error) -> Error {
Error::Consensus(e)
}
}

impl From<sp_blockchain::Error> for Error {
fn from(e: sp_blockchain::Error) -> Error {
Error::Blockchain(e)
}
}

impl From<sp_inherents::Error> for Error {
fn from(e: sp_inherents::Error) -> Error {
Error::Inherent(e)
}
}

impl From<SubsystemError> for Error {
fn from(e: SubsystemError) -> Error {
Error::Subsystem(e)
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Consensus(err) => write!(f, "consensus error: {}", err),
Self::Blockchain(err) => write!(f, "blockchain error: {}", err),
Self::Inherent(err) => write!(f, "inherent error: {:?}", err),
Self::Timeout => write!(f, "timeout: provisioner did not return inherent data after {:?}", PROPOSE_TIMEOUT),
Self::ClosedChannelFromProvisioner(err) => write!(f, "provisioner closed inherent data channel before sending: {}", err),
Self::Subsystem(err) => write!(f, "subsystem error: {:?}", err),
}
}
}

impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Consensus(err) => Some(err),
Self::Blockchain(err) => Some(err),
Self::ClosedChannelFromProvisioner(err) => Some(err),
_ => None
}
}
}
2 changes: 1 addition & 1 deletion node/service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ kusama-runtime = { path = "../../runtime/kusama" }
westend-runtime = { path = "../../runtime/westend" }
polkadot-network = { path = "../../network", optional = true }
polkadot-rpc = { path = "../../rpc" }
polkadot-node-core-proposer = { path = "../core/proposer" }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" }
Expand Down Expand Up @@ -56,7 +57,6 @@ sp-session = { git = "https://github.com/paritytech/substrate", branch = "master
sp-offchain = { package = "sp-offchain", git = "https://github.com/paritytech/substrate", branch = "master" }
prometheus-endpoint = { package = "substrate-prometheus-endpoint", git = "https://github.com/paritytech/substrate", branch = "master" }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" }
sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "master" }

[dev-dependencies]
polkadot-test-runtime-client = { path = "../../runtime/test-runtime/client" }
Expand Down
7 changes: 4 additions & 3 deletions node/service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use polkadot_subsystem::{
Subsystem, SubsystemContext, SpawnedSubsystem,
messages::{CandidateValidationMessage, CandidateBackingMessage},
};
use polkadot_node_core_proposer::ProposerFactory;
pub use service::{
Role, PruningMode, TransactionPoolOptions, Error, RuntimeGenesis,
TFullClient, TLightClient, TFullBackend, TLightBackend, TFullCallExecutor, TLightCallExecutor,
Expand Down Expand Up @@ -360,6 +361,7 @@ macro_rules! new_full {
.collect();

let (overseer, handler) = real_overseer(leaves, spawner)?;
let handler_clone = handler.clone();

task_manager.spawn_essential_handle().spawn_blocking("overseer", Box::pin(async move {
use futures::{pin_mut, select, FutureExt};
Expand All @@ -386,11 +388,10 @@ macro_rules! new_full {
let can_author_with =
consensus_common::CanAuthorWithNativeVersion::new(client.executor().clone());

// TODO: custom proposer (https://github.com/paritytech/polkadot/issues/1248)
let proposer = sc_basic_authorship::ProposerFactory::new(
let proposer = ProposerFactory::new(
client.clone(),
transaction_pool,
None,
handler_clone,
);

let babe_config = babe::BabeParams {
Expand Down
Loading