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 1 commit
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
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
))
)
}
}
101 changes: 32 additions & 69 deletions src/chain/chain_information/babe_genesis_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.

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

Expand All @@ -34,30 +34,6 @@ pub struct BabeGenesisConfiguration {
}

impl BabeGenesisConfiguration {
/// Retrieves the configuration from the storage of the genesis block.
///
/// Must be passed a closure that returns the storage value corresponding to the given key in
/// the genesis block storage.
pub fn from_genesis_storage(
mut genesis_storage_access: impl FnMut(&[u8]) -> Option<Vec<u8>>,
) -> Result<Self, FromGenesisStorageError> {
let wasm_code =
genesis_storage_access(b":code").ok_or(FromGenesisStorageError::RuntimeNotFound)?;
let heap_pages =
executor::storage_heap_pages_to_value(genesis_storage_access(b":heappages").as_deref())
.map_err(FromGenesisStorageError::HeapPagesDecode)?;
let vm = host::HostVmPrototype::new(host::Config {
module: &wasm_code,
heap_pages,
exec_hint: vm::ExecHint::Oneshot,
allow_unresolved_imports: false,
})
.map_err(FromGenesisStorageError::VmInitialization)?;
let (cfg, _) = Self::from_virtual_machine_prototype(vm, genesis_storage_access)
.map_err(FromGenesisStorageError::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 @@ -67,25 +43,35 @@ impl BabeGenesisConfiguration {
pub fn from_virtual_machine_prototype(
vm: host::HostVmPrototype,
mut genesis_storage_access: impl FnMut(&[u8]) -> Option<Vec<u8>>,
) -> Result<(Self, host::HostVmPrototype), FromVmPrototypeError> {
let mut vm: host::HostVm = vm
.run_no_param("BabeApi_configuration")
.map_err(|(err, proto)| FromVmPrototypeError::VmStart(err, proto))?
.into();
) -> (Result<Self, FromVmPrototypeError>, host::HostVmPrototype) {
let mut vm: host::HostVm = match vm.run_no_param("BabeApi_configuration") {
Ok(vm) => vm.into(),
Err((err, proto)) => return (Err(FromVmPrototypeError::VmStart(err)), proto),
};

loop {
match vm {
host::HostVm::ReadyToRun(r) => vm = r.run(),
host::HostVm::Finished(finished) => {
let output = finished.value();
let cfg =
nom::combinator::all_consuming(decode_genesis_config)(output.as_ref())
.map(|(_, parse_result)| parse_result)
.map_err(|_| FromVmPrototypeError::OutputDecode)?;
drop(output);
break Ok((cfg, finished.into_prototype()));
let cfg = {
let output = finished.value();
let val = match nom::combinator::all_consuming(decode_genesis_config)(
output.as_ref(),
) {
Ok((_, parse_result)) => Ok(parse_result),
Err(_) => Err(FromVmPrototypeError::OutputDecode),
};
// Note: this is a bit convoluted, but I have no idea how to satisfy the
// borrow checker other than by doing so.
drop(output);
val
};

break (cfg, finished.into_prototype());
}
host::HostVm::Error { prototype, .. } => {
break (Err(FromVmPrototypeError::Trapped), prototype)
}
host::HostVm::Error { .. } => break Err(FromVmPrototypeError::Trapped),

host::HostVm::ExternalStorageGet(req) => {
let value = genesis_storage_access(req.key().as_ref());
Expand All @@ -97,41 +83,21 @@ impl BabeGenesisConfiguration {
}
host::HostVm::LogEmit(req) => vm = req.resume(),

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

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

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

/// Error when retrieving the BABE configuration.
#[derive(Debug, derive_more::Display)]
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 @@ -145,12 +111,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
))
)
}
}
Expand Down
Loading