diff --git a/Cargo.lock b/Cargo.lock index c734837601ee..b28b6d5ce582 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4482,6 +4482,17 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "polkadot-node-bitfield-signing" +version = "0.1.0" +dependencies = [ + "futures 0.3.5", + "log 0.4.8", + "polkadot-node-subsystem", + "polkadot-primitives", + "wasm-timer", +] + [[package]] name = "polkadot-node-core-backing" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index f892d7f6b6a6..698f9d08d761 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ members = [ "service", "validation", + "node/bitfield-signing", "node/core/proposer", "node/network/bridge", "node/network/pov-distribution", diff --git a/node/bitfield-signing/Cargo.toml b/node/bitfield-signing/Cargo.toml new file mode 100644 index 000000000000..52ef2b8a284e --- /dev/null +++ b/node/bitfield-signing/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "polkadot-node-bitfield-signing" +version = "0.1.0" +authors = ["Peter Goodspeed-Niklaus "] +edition = "2018" + +[dependencies] +futures = "0.3.5" +log = "0.4.8" +polkadot-primitives = { path = "../../primitives" } +polkadot-node-subsystem = { path = "../subsystem" } +wasm-timer = "0.2.4" diff --git a/node/bitfield-signing/src/lib.rs b/node/bitfield-signing/src/lib.rs new file mode 100644 index 000000000000..ba8e9a905c24 --- /dev/null +++ b/node/bitfield-signing/src/lib.rs @@ -0,0 +1,136 @@ +// Copyright 2020 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 . + +//! The bitfield signing subsystem produces `SignedAvailabilityBitfield`s once per block. + +use futures::{ + channel::{mpsc, oneshot}, + future::{abortable, AbortHandle}, + prelude::*, + Future, +}; +use polkadot_node_subsystem::{ + messages::{AllMessages, BitfieldSigningMessage}, + OverseerSignal, SubsystemResult, +}; +use polkadot_node_subsystem::{FromOverseer, SpawnedSubsystem, Subsystem, SubsystemContext}; +use polkadot_primitives::Hash; +use std::{ + collections::HashMap, + pin::Pin, + time::{Duration, Instant}, +}; + +/// Delay between starting a bitfield signing job and its attempting to create a bitfield. +const JOB_DELAY: Duration = Duration::from_millis(1500); + +/// JobCanceler aborts all abort handles on drop. +#[derive(Debug, Default)] +struct JobCanceler(HashMap); + +// AbortHandle doesn't impl Drop on its own, so we wrap it +// in this struct to get free cancellation on drop. +impl Drop for JobCanceler { + fn drop(&mut self) { + for abort_handle in self.0.values() { + abort_handle.abort(); + } + } +} + +/// Bitfield signing subsystem. +struct BitfieldSigning; + +impl BitfieldSigning { + async fn run(mut ctx: Context) -> SubsystemResult<()> + where + Context: SubsystemContext + Clone, + { + let mut active_jobs = JobCanceler::default(); + + loop { + use FromOverseer::*; + use OverseerSignal::*; + match ctx.recv().await { + Ok(Communication { msg: _ }) => { + unreachable!("BitfieldSigningMessage is uninstantiable; qed") + } + Ok(Signal(StartWork(hash))) => { + let (future, abort_handle) = + abortable(bitfield_signing_job(hash.clone(), ctx.clone())); + // future currently returns a Result based on whether or not it was aborted; + // let's ignore all that and return () unconditionally, to fit the interface. + let future = async move { + let _ = future.await; + }; + active_jobs.0.insert(hash.clone(), abort_handle); + ctx.spawn(Box::pin(future)).await?; + } + Ok(Signal(StopWork(hash))) => { + if let Some(abort_handle) = active_jobs.0.remove(&hash) { + abort_handle.abort(); + } + } + Ok(Signal(Conclude)) => break, + Err(err) => { + return Err(err); + } + } + } + + Ok(()) + } +} + +impl Subsystem for BitfieldSigning +where + Context: SubsystemContext + Clone, +{ + fn start(self, ctx: Context) -> SpawnedSubsystem { + SpawnedSubsystem(Box::pin(async move { + if let Err(err) = Self::run(ctx).await { + log::error!("{:?}", err); + }; + })) + } +} + +async fn bitfield_signing_job(hash: Hash, ctx: Context) +where + Context: SubsystemContext, +{ + // first up, figure out when we need to wait until + let delay = wasm_timer::Delay::new_at(Instant::now() + JOB_DELAY); + // next, do some prerequisite work + todo!(); + // now, wait for the delay to be complete + if let Err(_) = delay.await { + return; + } + // let (tx, _) = oneshot::channel(); + + // ctx.send_message(AllMessages::CandidateValidation( + // CandidateValidationMessage::Validate( + // Default::default(), + // Default::default(), + // PoVBlock { + // block_data: BlockData(Vec::new()), + // }, + // tx, + // ) + // )).await.unwrap(); + unimplemented!() +} diff --git a/node/subsystem/src/messages.rs b/node/subsystem/src/messages.rs index 2040b413488d..eeff74ca341d 100644 --- a/node/subsystem/src/messages.rs +++ b/node/subsystem/src/messages.rs @@ -220,6 +220,19 @@ impl BitfieldDistributionMessage { } } +/// Bitfield signing message. +/// +/// Currently non-instantiable. +#[derive(Debug)] +pub enum BitfieldSigningMessage {} + +impl BitfieldSigningMessage { + /// If the current variant contains the relay parent hash, return it. + pub fn relay_parent(&self) -> Option { + None + } +} + /// Availability store subsystem message. #[derive(Debug)] pub enum AvailabilityStoreMessage { @@ -398,6 +411,8 @@ pub enum AllMessages { AvailabilityDistribution(AvailabilityDistributionMessage), /// Message for the bitfield distribution subsystem. BitfieldDistribution(BitfieldDistributionMessage), + /// Message for the bitfield signing subsystem. + BitfieldSigning(BitfieldSigningMessage), /// Message for the Provisioner subsystem. Provisioner(ProvisionerMessage), /// Message for the PoV Distribution subsystem. diff --git a/roadmap/implementers-guide/src/node/availability/bitfield-signing.md b/roadmap/implementers-guide/src/node/availability/bitfield-signing.md index 24c9d8dbedc8..856139589636 100644 --- a/roadmap/implementers-guide/src/node/availability/bitfield-signing.md +++ b/roadmap/implementers-guide/src/node/availability/bitfield-signing.md @@ -22,9 +22,8 @@ Upon onset of a new relay-chain head with `StartWork`, launch bitfield signing j Localized to a specific relay-parent `r` If not running as a validator, do nothing. +- Begin by waiting a fixed period of time so availability distribution has the chance to make candidates available. - Determine our validator index `i`, the set of backed candidates pending availability in `r`, and which bit of the bitfield each corresponds to. - Start with an empty bitfield. For each bit in the bitfield, if there is a candidate pending availability, query the [Availability Store](../utility/availability-store.md) for whether we have the availability chunk for our validator index. - For all chunks we have, set the corresponding bit in the bitfield. - Sign the bitfield and dispatch a `BitfieldDistribution::DistributeBitfield` message. - -Note that because the bitfield signing job is launched once per block and executes immediately, the signed availability statement for block `N` is best considered as a report of the candidates available at the end of block `N-1`.