Adds --no-validator CLI flag (#3348)

* Implement `is_validator` for offchain-workers

* Introduce `--no-validator` flag

* Don't run babe/grandpa/im-online when `--no-validator` is given

* Fixes compilation

* Bump spec version

* Improve error handling in executor

* Add missing extern function

* Revert making error public

* Remove `--no-validator` CLI
This commit is contained in:
Bastian Köcher
2019-08-09 14:24:18 +02:00
committed by GitHub
parent b4b53cbb6e
commit c824c959d7
18 changed files with 97 additions and 28 deletions
+2 -2
View File
@@ -436,6 +436,8 @@ where
),
};
let is_dev = cli.shared_params.dev;
let role = if cli.light {
service::Roles::LIGHT
} else {
@@ -462,8 +464,6 @@ where
config.roles = role;
config.disable_grandpa = cli.no_grandpa;
let is_dev = cli.shared_params.dev;
let client_id = config.client_id();
fill_network_configuration(
cli.network_config,
+2 -2
View File
@@ -38,8 +38,8 @@ pub enum Error {
#[display(fmt="Method not found: '{}'", _0)]
MethodNotFound(String),
/// Code is invalid (expected single byte)
#[display(fmt="Invalid Code")]
InvalidCode,
#[display(fmt="Invalid Code: {}", _0)]
InvalidCode(String),
/// Could not get runtime version.
#[display(fmt="On-chain runtime does not specify version")]
VersionInvalid,
@@ -22,7 +22,7 @@ use runtime_version::{NativeVersion, RuntimeVersion};
use codec::{Decode, Encode};
use crate::RuntimeInfo;
use primitives::{Blake2Hasher, NativeOrEncoded};
use log::trace;
use log::{trace, warn};
use crate::RuntimesCache;
@@ -107,8 +107,14 @@ impl<D: NativeExecutionDispatch> RuntimeInfo for NativeExecutor<D> {
) -> Option<RuntimeVersion> {
RUNTIMES_CACHE.with(|cache| {
let cache = &mut cache.borrow_mut();
cache.fetch_runtime(&self.fallback, ext, self.default_heap_pages)
.ok()?.version().clone()
match cache.fetch_runtime(&self.fallback, ext, self.default_heap_pages) {
Ok(runtime) => runtime.version(),
Err(e) => {
warn!(target: "executor", "Failed to fetch runtime: {:?}", e);
None
}
}
})
}
}
@@ -876,6 +876,13 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(0)
},
ext_is_validator() -> u32 => {
Ok(if runtime_io::is_validator() {
1
} else {
0
})
},
ext_submit_transaction(msg_data: *const u8, len: u32) -> u32 => {
let extrinsic = this.memory.get(msg_data, len as usize)
.map_err(|_| "OOB while ext_submit_transaction: wasm")?;
@@ -248,12 +248,12 @@ impl RuntimesCache {
) -> Result<Rc<CachedRuntime>, Error> {
let code_hash = ext
.original_storage_hash(well_known_keys::CODE)
.ok_or(Error::InvalidCode)?;
.ok_or(Error::InvalidCode("`CODE` not found in storage.".into()))?;
// This is direct result from fighting with borrowck.
let handle_result =
|cached_result: &Result<Rc<CachedRuntime>, CacheError>| match *cached_result {
Err(_) => Err(Error::InvalidCode),
Err(ref e) => Err(Error::InvalidCode(format!("{:?}", e))),
Ok(ref cached_runtime) => Ok(Rc::clone(cached_runtime)),
};
+13 -2
View File
@@ -47,11 +47,15 @@ pub(crate) struct Api<Storage, Block: traits::Block> {
db: Storage,
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
_at: BlockId<Block>,
/// Is this node a potential validator?
is_validator: bool,
}
fn unavailable_yet<R: Default>(name: &str) -> R {
error!("The {:?} API is not available for offchain workers yet. Follow \
https://github.com/paritytech/substrate/issues/1458 for details", name);
error!(
"The {:?} API is not available for offchain workers yet. Follow \
https://github.com/paritytech/substrate/issues/1458 for details", name
);
Default::default()
}
@@ -63,6 +67,10 @@ where
Storage: OffchainStorage,
Block: traits::Block,
{
fn is_validator(&self) -> bool {
self.is_validator
}
fn submit_transaction(&mut self, ext: Vec<u8>) -> Result<(), ()> {
self.sender
.unbounded_send(ExtMessage::SubmitExtrinsic(ext))
@@ -277,6 +285,7 @@ impl<A: ChainApi> AsyncApi<A> {
db: S,
at: BlockId<A::Block>,
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
is_validator: bool,
) -> (Api<S, A::Block>, AsyncApi<A>) {
let (sender, rx) = mpsc::unbounded();
@@ -285,6 +294,7 @@ impl<A: ChainApi> AsyncApi<A> {
db,
network_state,
_at: at,
is_validator,
};
let async_api = AsyncApi {
@@ -362,6 +372,7 @@ mod tests {
db,
BlockId::Number(Zero::zero()),
mock,
false,
)
}
+4 -4
View File
@@ -98,9 +98,8 @@ impl<Client, Storage, Block> OffchainWorkers<
number: &<Block::Header as traits::Header>::Number,
pool: &Arc<Pool<A>>,
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
) -> impl Future<Output = ()> where
A: ChainApi<Block=Block> + 'static,
{
is_validator: bool,
) -> impl Future<Output = ()> where A: ChainApi<Block=Block> + 'static {
let runtime = self.client.runtime_api();
let at = BlockId::number(*number);
let has_api = runtime.has_api::<dyn OffchainWorkerApi<Block>>(&at);
@@ -112,6 +111,7 @@ impl<Client, Storage, Block> OffchainWorkers<
self.db.clone(),
at.clone(),
network_state.clone(),
is_validator,
);
debug!("Spawning offchain workers at {:?}", at);
let number = *number;
@@ -177,7 +177,7 @@ mod tests {
// when
let offchain = OffchainWorkers::new(client, db);
futures::executor::block_on(offchain.on_block_imported(&0u64, &pool, network_state));
futures::executor::block_on(offchain.on_block_imported(&0u64, &pool, network_state, false));
// then
assert_eq!(pool.status().ready, 1);
+4
View File
@@ -134,6 +134,10 @@ impl TestOffchainExt {
}
impl offchain::Externalities for TestOffchainExt {
fn is_validator(&self) -> bool {
unimplemented!("not needed in tests so far")
}
fn submit_transaction(&mut self, _ex: Vec<u8>) -> Result<(), ()> {
unimplemented!("not needed in tests so far")
}
@@ -232,6 +232,11 @@ impl Timestamp {
/// An extended externalities for offchain workers.
pub trait Externalities {
/// Returns if the local node is a potential validator.
///
/// Even if this function returns `true`, it does not mean that any keys are configured
/// and that the validator is registered in the chain.
fn is_validator(&self) -> bool;
/// Submit transaction.
///
/// The transaction will end up in the pool and be propagated to others.
@@ -349,6 +354,10 @@ pub trait Externalities {
}
impl<T: Externalities + ?Sized> Externalities for Box<T> {
fn is_validator(&self) -> bool {
(& **self).is_validator()
}
fn submit_transaction(&mut self, ex: Vec<u8>) -> Result<(), ()> {
(&mut **self).submit_transaction(ex)
}
+3 -1
View File
@@ -268,6 +268,7 @@ pub trait OffchainWorker<C: Components> {
>,
pool: &Arc<TransactionPool<C::TransactionPoolApi>>,
network_state: &Arc<dyn NetworkStateInfo + Send + Sync>,
is_validator: bool,
) -> error::Result<Box<dyn Future<Item = (), Error = ()> + Send>>;
}
@@ -284,8 +285,9 @@ impl<C: Components> OffchainWorker<Self> for C where
>,
pool: &Arc<TransactionPool<C::TransactionPoolApi>>,
network_state: &Arc<dyn NetworkStateInfo + Send + Sync>,
is_validator: bool,
) -> error::Result<Box<dyn Future<Item = (), Error = ()> + Send>> {
let future = offchain.on_block_imported(number, pool, network_state.clone())
let future = offchain.on_block_imported(number, pool, network_state.clone(), is_validator)
.map(|()| Ok(()));
Ok(Box::new(Compat::new(future)))
}
+3 -1
View File
@@ -153,7 +153,7 @@ impl<Components: components::Components> Service<Components> {
pub fn new(
mut config: FactoryFullConfiguration<Components::Factory>,
) -> Result<Self, error::Error> {
let (signal, exit) = ::exit_future::signal();
let (signal, exit) = exit_future::signal();
// List of asynchronous tasks to spawn. We collect them, then spawn them all at once.
let (to_spawn_tx, to_spawn_rx) =
@@ -250,6 +250,7 @@ impl<Components: components::Components> Service<Components> {
let offchain = offchain_workers.as_ref().map(Arc::downgrade);
let to_spawn_tx_ = to_spawn_tx.clone();
let network_state_info: Arc<dyn NetworkStateInfo + Send + Sync> = network.clone();
let is_validator = config.roles.is_authority();
let events = client.import_notification_stream()
.map(|v| Ok::<_, ()>(v)).compat()
@@ -270,6 +271,7 @@ impl<Components: components::Components> Service<Components> {
&offchain,
&txpool,
&network_state_info,
is_validator,
).map_err(|e| warn!("Offchain workers error processing new block: {:?}", e))?;
let _ = to_spawn_tx_.unbounded_send(future);
}
+5
View File
@@ -273,6 +273,11 @@ export_api! {
export_api! {
pub(crate) trait OffchainApi {
/// Returns if the local node is a potential validator.
///
/// Even if this function returns `true`, it does not mean that any keys are configured
/// and that the validator is registered in the chain.
fn is_validator() -> bool;
/// Submit transaction to the pool.
///
/// The transaction will end up in the pool.
+6
View File
@@ -336,6 +336,12 @@ fn with_offchain<R>(f: impl FnOnce(&mut dyn offchain::Externalities) -> R, msg:
}
impl OffchainApi for () {
fn is_validator() -> bool {
with_offchain(|ext| {
ext.is_validator()
}, "is_validator can be called only in the offchain worker context")
}
fn submit_transaction<T: codec::Encode>(data: &T) -> Result<(), ()> {
with_offchain(|ext| {
ext.submit_transaction(codec::Encode::encode(data))
+10
View File
@@ -432,6 +432,12 @@ pub mod ext {
// Offchain-worker Context
//================================
/// Returns if the local node is a potential validator.
///
/// - `1` == `true`
/// - `0` == `false`
fn ext_is_validator() -> u32;
/// Submit transaction.
///
/// # Returns
@@ -964,6 +970,10 @@ impl CryptoApi for () {
}
impl OffchainApi for () {
fn is_validator() -> bool {
unsafe { ext_is_validator.get()() == 1 }
}
fn submit_transaction<T: codec::Encode>(data: &T) -> Result<(), ()> {
let encoded_data = codec::Encode::encode(data);
let ret = unsafe {
+4
View File
@@ -263,6 +263,10 @@ impl NeverOffchainExt {
}
impl offchain::Externalities for NeverOffchainExt {
fn is_validator(&self) -> bool {
unreachable!()
}
fn submit_transaction(&mut self, _extrinsic: Vec<u8>) -> Result<(), ()> {
unreachable!()
}
+8 -8
View File
@@ -138,15 +138,15 @@ construct_service_factory! {
let select = babe.select(service.on_exit()).then(|_| Ok(()));
service.spawn_task(Box::new(select));
let config = grandpa::Config {
// FIXME #1578 make this available through chainspec
gossip_duration: Duration::from_millis(333),
justification_period: 4096,
name: Some(service.config().name.clone()),
keystore: Some(service.keystore()),
};
if !service.config().disable_grandpa {
let config = grandpa::Config {
// FIXME #1578 make this available through chainspec
gossip_duration: Duration::from_millis(333),
justification_period: 4096,
name: Some(service.config().name.clone()),
keystore: Some(service.keystore()),
};
if service.config().roles.is_authority() {
let telemetry_on_connect = TelemetryOnConnect {
telemetry_connection_sinks: service.telemetry_on_connect_stream(),
+2 -2
View File
@@ -80,8 +80,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// and set impl_version to equal spec_version. If only runtime
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 136,
impl_version: 136,
spec_version: 137,
impl_version: 137,
apis: RUNTIME_API_VERSIONS,
};
+4 -1
View File
@@ -219,7 +219,10 @@ decl_module! {
// Runs after every block.
fn offchain_worker(now: T::BlockNumber) {
Self::offchain(now);
// Only send messages if we are a potential validator.
if sr_io::is_validator() {
Self::offchain(now);
}
}
}
}