Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize building ChainInformation and return runtime #2270

Merged
merged 4 commits into from
May 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions bin/full-node/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pub async fn run(cli_options: cli::CliOptionsRun) {
};

// TODO: don't unwrap?
let genesis_chain_information = chain_spec.as_chain_information().unwrap();
let genesis_chain_information = chain_spec.as_chain_information().unwrap().0;

// If `chain_spec` define a parachain, also load the specs of the relay chain.
let (relay_chain_spec, _parachain_id) =
Expand Down Expand Up @@ -138,7 +138,7 @@ pub async fn run(cli_options: cli::CliOptionsRun) {
// TODO: don't unwrap?
let relay_genesis_chain_information = relay_chain_spec
.as_ref()
.map(|relay_chain_spec| relay_chain_spec.as_chain_information().unwrap());
.map(|relay_chain_spec| relay_chain_spec.as_chain_information().unwrap().0);

let threads_pool = futures::executor::ThreadPool::builder()
.name_prefix("tasks-pool-")
Expand Down
2 changes: 1 addition & 1 deletion bin/light-base/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ impl<TChain, TPlat: Platform> Client<TChain, TPlat> {
match (
chain_spec
.as_chain_information() // TODO: very expensive, don't always call?
.map(chain::chain_information::ValidChainInformation::try_from),
.map(|(ci, _)| chain::chain_information::ValidChainInformation::try_from(ci)), // TODO: don't just throw away the runtime
chain_spec.light_sync_state().map(|s| {
chain::chain_information::ValidChainInformation::try_from(
s.as_chain_information(),
Expand Down
1 change: 1 addition & 0 deletions bin/wasm-node/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Changed

- The runtime of the genesis block is now only compiled once when a chain is added, decreasing the time this operation takes. ([#2270](https://github.com/paritytech/smoldot/pull/2270))
- Block announces are now propagated to other peers that are also light clients. Light clients should try to connect to as few full nodes as possible (to save resources), but doing so can leave them vulnerable to eclipse attacks. By having light clients connect to other light clients and making them gossip block announces to each other, we increase the likelihood that they detect situations where a given validator generates two blocks during the same slot and is trying to show one of the block only to some peers and the other block to the rest. ([#2226](https://github.com/paritytech/smoldot/pull/2226))

## 0.6.15 - 2022-04-07
Expand Down
4 changes: 2 additions & 2 deletions src/chain/chain_information.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ pub enum ChainInformationConsensus {
///
/// If the finalized block belongs to epoch #0, which starts at block #1, then this must
/// contain the information about the epoch #0, which can be found by calling
/// [`babe_genesis_config::BabeGenesisConfiguration::from_genesis_storage`].
/// [`babe_genesis_config::BabeGenesisConfiguration::from_virtual_machine_prototype`].
///
/// Must be `None` if and only if the finalized block is block #0.
///
Expand All @@ -213,7 +213,7 @@ pub enum ChainInformationConsensus {
///
/// If the finalized block is block #0, then this must contain the information about the
/// epoch #0, which can be found by calling
/// [`babe_genesis_config::BabeGenesisConfiguration::from_genesis_storage`].
/// [`babe_genesis_config::BabeGenesisConfiguration::from_virtual_machine_prototype`].
finalized_next_epoch_transition: BabeEpochInformation,
},
}
Expand Down
135 changes: 56 additions & 79 deletions src/chain/chain_information/aura_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
//! It can be used on any block.

use crate::{
executor::{self, host, vm},
executor::{host, vm},
header,
};

Expand All @@ -40,29 +40,6 @@ pub struct AuraConfiguration {
}

impl AuraConfiguration {
/// Retrieves the configuration from the storage of a block.
///
/// Must be passed a closure that returns the storage value corresponding to the given key in
/// the block storage.
pub fn from_storage(
mut storage_access: impl FnMut(&[u8]) -> Option<Vec<u8>>,
) -> Result<Self, FromStorageError> {
let wasm_code = storage_access(b":code").ok_or(FromStorageError::RuntimeNotFound)?;
let heap_pages =
executor::storage_heap_pages_to_value(storage_access(b":heappages").as_deref())
.map_err(FromStorageError::HeapPagesDecode)?;
let vm = host::HostVmPrototype::new(host::Config {
module: &wasm_code,
heap_pages,
exec_hint: vm::ExecHint::Oneshot,
allow_unresolved_imports: false,
})
.map_err(FromStorageError::VmInitialization)?;
let (cfg, _) = Self::from_virtual_machine_prototype(vm, storage_access)
.map_err(FromStorageError::VmError)?;
Ok(cfg)
}

/// Retrieves the configuration from the given virtual machine prototype.
///
/// Must be passed a closure that returns the storage value corresponding to the given key in
Expand All @@ -72,24 +49,36 @@ impl AuraConfiguration {
pub fn from_virtual_machine_prototype(
vm: host::HostVmPrototype,
mut storage_access: impl FnMut(&[u8]) -> Option<Vec<u8>>,
) -> Result<(Self, host::HostVmPrototype), FromVmPrototypeError> {
let mut vm: host::HostVm = vm
.run_no_param("AuraApi_slot_duration")
.map_err(|(err, proto)| FromVmPrototypeError::VmStart(err, proto))?
.into();
) -> (Result<Self, FromVmPrototypeError>, host::HostVmPrototype) {
let mut vm: host::HostVm = match vm.run_no_param("AuraApi_slot_duration") {
Ok(vm) => vm.into(),
Err((err, proto)) => return (Err(FromVmPrototypeError::VmStart(err)), proto),
};

let (slot_duration, vm_prototype) = loop {
match vm {
host::HostVm::ReadyToRun(r) => vm = r.run(),
host::HostVm::Finished(finished) => {
let slot_duration = NonZeroU64::new(u64::from_le_bytes(
<[u8; 8]>::try_from(finished.value().as_ref())
.map_err(|_| FromVmPrototypeError::BadSlotDuration)?,
))
.ok_or(FromVmPrototypeError::BadSlotDuration)?;
break (slot_duration, finished.into_prototype());
let convert_attempt = <[u8; 8]>::try_from(finished.value().as_ref());
let vm_prototype = finished.into_prototype();

let slot_duration = match convert_attempt {
Ok(val) => match NonZeroU64::new(u64::from_le_bytes(val)) {
Some(val) => val,
None => {
return (Err(FromVmPrototypeError::BadSlotDuration), vm_prototype)
}
},
Err(_) => {
return (Err(FromVmPrototypeError::BadSlotDuration), vm_prototype)
}
};

break (slot_duration, vm_prototype);
}
host::HostVm::Error { prototype, .. } => {
return (Err(FromVmPrototypeError::Trapped), prototype)
}
host::HostVm::Error { .. } => return Err(FromVmPrototypeError::Trapped),

host::HostVm::ExternalStorageGet(req) => {
let value = storage_access(req.key().as_ref());
Expand All @@ -101,27 +90,38 @@ impl AuraConfiguration {
}
host::HostVm::LogEmit(req) => vm = req.resume(),

_ => return Err(FromVmPrototypeError::HostFunctionNotAllowed),
other => {
let prototype = other.into_prototype();
return (Err(FromVmPrototypeError::HostFunctionNotAllowed), prototype);
}
}
};

let mut vm: host::HostVm = vm_prototype
.run_no_param("AuraApi_authorities")
.map_err(|(err, proto)| FromVmPrototypeError::VmStart(err, proto))?
.into();
let mut vm: host::HostVm = match vm_prototype.run_no_param("AuraApi_authorities") {
Ok(vm) => vm.into(),
Err((err, proto)) => return (Err(FromVmPrototypeError::VmStart(err)), proto),
};

let (authorities_list, vm_prototype) = loop {
match vm {
host::HostVm::ReadyToRun(r) => vm = r.run(),
host::HostVm::Finished(finished) => {
let authorities_list =
header::AuraAuthoritiesIter::decode(finished.value().as_ref())
.map_err(|_| FromVmPrototypeError::AuthoritiesListDecodeError)?
.map(header::AuraAuthority::from)
.collect::<Vec<_>>();
break (authorities_list, finished.into_prototype());
match header::AuraAuthoritiesIter::decode(finished.value().as_ref()) {
Ok(iter) => {
Ok(iter.map(header::AuraAuthority::from).collect::<Vec<_>>())
}
Err(_) => Err(FromVmPrototypeError::AuthoritiesListDecodeError),
};

match authorities_list {
Ok(l) => break (l, finished.into_prototype()),
Err(err) => return (Err(err), finished.into_prototype()),
}
}
host::HostVm::Error { prototype, .. } => {
return (Err(FromVmPrototypeError::Trapped), prototype)
}
host::HostVm::Error { .. } => return Err(FromVmPrototypeError::Trapped),

host::HostVm::ExternalStorageGet(req) => {
let value = storage_access(req.key().as_ref());
Expand All @@ -133,7 +133,10 @@ impl AuraConfiguration {
}
host::HostVm::LogEmit(req) => vm = req.resume(),

_ => return Err(FromVmPrototypeError::HostFunctionNotAllowed),
other => {
let prototype = other.into_prototype();
return (Err(FromVmPrototypeError::HostFunctionNotAllowed), prototype);
}
}
};

Expand All @@ -142,30 +145,7 @@ impl AuraConfiguration {
slot_duration,
};

Ok((outcome, vm_prototype))
}
}

/// Error when retrieving the Aura configuration.
#[derive(Debug, derive_more::Display)]
pub enum FromStorageError {
/// Runtime couldn't be found in the storage.
RuntimeNotFound,
/// Failed to decode heap pages from the storage.
HeapPagesDecode(executor::InvalidHeapPagesError),
/// Error when initializing the virtual machine.
VmInitialization(host::NewErr),
/// Error while executing the runtime.
VmError(FromVmPrototypeError),
}

impl FromStorageError {
/// Returns `true` if this error is about an invalid function.
pub fn is_function_not_found(&self) -> bool {
match self {
FromStorageError::VmError(err) => err.is_function_not_found(),
_ => false,
}
(Ok(outcome), vm_prototype)
}
}

Expand All @@ -174,7 +154,7 @@ impl FromStorageError {
pub enum FromVmPrototypeError {
/// Error when starting the virtual machine.
#[display(fmt = "{}", _0)]
VmStart(host::StartErr, host::HostVmPrototype),
VmStart(host::StartErr),
/// Crash while running the virtual machine.
Trapped,
/// Virtual machine tried to call a host function that isn't valid in this context.
Expand All @@ -190,12 +170,9 @@ impl FromVmPrototypeError {
pub fn is_function_not_found(&self) -> bool {
matches!(
self,
FromVmPrototypeError::VmStart(
host::StartErr::VirtualMachine(
vm::StartErr::FunctionNotFound | vm::StartErr::NotAFunction
),
_
)
FromVmPrototypeError::VmStart(host::StartErr::VirtualMachine(
vm::StartErr::FunctionNotFound | vm::StartErr::NotAFunction
))
)
}
}
Loading