WIP soft limits triggering is signalled via AVMOutcome

This commit is contained in:
Roman Nozdrin
2024-02-07 17:19:24 +00:00
parent 81019e6633
commit 62ff7ebba7
15 changed files with 238 additions and 64 deletions

View File

@ -25,6 +25,7 @@ use crate::INTERPRETER_SUCCESS;
use air_interpreter_data::InterpreterDataEnvelope;
use air_interpreter_interface::CallRequests;
use air_interpreter_interface::CallRequestsRepr;
use air_interpreter_interface::SoftLimitsTriggering;
use air_interpreter_sede::ToSerialized;
use air_interpreter_signatures::KeyPair;
use air_utils::measure;
@ -41,6 +42,7 @@ pub(crate) fn from_success_result(
exec_ctx: ExecutionCtx<'_>,
trace_handler: TraceHandler,
keypair: &KeyPair,
soft_limits_triggering: SoftLimitsTriggering,
) -> Result<InterpreterOutcome, InterpreterOutcome> {
let (ret_code, error_message) = if exec_ctx.call_results.is_empty() {
(INTERPRETER_SUCCESS, String::new())
@ -49,7 +51,14 @@ pub(crate) fn from_success_result(
(farewell_error.to_error_code(), farewell_error.to_string())
};
let outcome = populate_outcome_from_contexts(exec_ctx, trace_handler, ret_code, error_message, keypair);
let outcome = populate_outcome_from_contexts(
exec_ctx,
trace_handler,
ret_code,
error_message,
keypair,
soft_limits_triggering,
);
Ok(outcome)
}
@ -59,6 +68,7 @@ pub(crate) fn from_success_result(
pub(crate) fn from_uncatchable_error(
data: impl Into<Vec<u8>> + Debug,
error: impl ToErrorCode + ToString + Debug,
soft_limits_triggering: SoftLimitsTriggering,
) -> InterpreterOutcome {
let ret_code = error.to_error_code();
let data = data.into();
@ -66,7 +76,14 @@ pub(crate) fn from_uncatchable_error(
.serialize(&CallRequests::new())
.expect("default serializer shouldn't fail");
InterpreterOutcome::new(ret_code, error.to_string(), data, vec![], call_requests)
InterpreterOutcome::new(
ret_code,
error.to_string(),
data,
vec![],
call_requests,
soft_limits_triggering,
)
}
/// Create InterpreterOutcome from supplied execution context, trace handler, and error,
@ -77,6 +94,7 @@ pub(crate) fn from_execution_error(
trace_handler: TraceHandler,
error: impl ToErrorCode + ToString + Debug,
keypair: &KeyPair,
soft_limits_triggering: SoftLimitsTriggering,
) -> InterpreterOutcome {
populate_outcome_from_contexts(
exec_ctx,
@ -84,6 +102,7 @@ pub(crate) fn from_execution_error(
error.to_error_code(),
error.to_string(),
keypair,
soft_limits_triggering,
)
}
@ -94,13 +113,14 @@ fn populate_outcome_from_contexts(
ret_code: i64,
error_message: String,
keypair: &KeyPair,
soft_limits_triggering: SoftLimitsTriggering,
) -> InterpreterOutcome {
match compactify_streams(&mut exec_ctx, &mut trace_handler) {
match compactify_streams(&mut exec_ctx, &mut trace_handler, soft_limits_triggering) {
Ok(()) => {}
Err(outcome) => return outcome,
};
match sign_result(&mut exec_ctx, keypair) {
match sign_result(&mut exec_ctx, keypair, soft_limits_triggering) {
Ok(()) => {}
Err(outcome) => return outcome,
};
@ -126,22 +146,37 @@ fn populate_outcome_from_contexts(
tracing::Level::INFO,
"CallRequestsRepr.serialize",
);
InterpreterOutcome::new(ret_code, error_message, data, next_peer_pks, call_requests)
InterpreterOutcome::new(
ret_code,
error_message,
data,
next_peer_pks,
call_requests,
soft_limits_triggering,
)
}
fn compactify_streams(exec_ctx: &mut ExecutionCtx<'_>, trace_ctx: &mut TraceHandler) -> Result<(), InterpreterOutcome> {
fn compactify_streams(
exec_ctx: &mut ExecutionCtx<'_>,
trace_ctx: &mut TraceHandler,
soft_limits_triggering: SoftLimitsTriggering,
) -> Result<(), InterpreterOutcome> {
exec_ctx
.streams
.compactify(trace_ctx)
.and_then(|_| exec_ctx.stream_maps.compactify(trace_ctx))
.map_err(execution_error_into_outcome)
.map_err(|err| execution_error_into_outcome(err, soft_limits_triggering))
}
fn sign_result(exec_ctx: &mut ExecutionCtx<'_>, keypair: &KeyPair) -> Result<(), InterpreterOutcome> {
fn sign_result(
exec_ctx: &mut ExecutionCtx<'_>,
keypair: &KeyPair,
soft_limits_triggering: SoftLimitsTriggering,
) -> Result<(), InterpreterOutcome> {
let current_signature = exec_ctx
.peer_cid_tracker
.gen_signature(&exec_ctx.run_parameters.salt, keypair)
.map_err(signing_error_into_outcome)?;
.map_err(|err| signing_error_into_outcome(err, soft_limits_triggering))?;
let current_pubkey = keypair.public();
exec_ctx.signature_store.put(current_pubkey, current_signature);
@ -151,12 +186,29 @@ fn sign_result(exec_ctx: &mut ExecutionCtx<'_>, keypair: &KeyPair) -> Result<(),
// these methods are called only if there is an internal error in the interpreter and
// new execution trace was corrupted
fn execution_error_into_outcome(error: ExecutionError) -> InterpreterOutcome {
InterpreterOutcome::new(error.to_error_code(), error.to_string(), vec![], vec![], <_>::default())
fn execution_error_into_outcome(
error: ExecutionError,
soft_limits_triggering: SoftLimitsTriggering,
) -> InterpreterOutcome {
InterpreterOutcome::new(
error.to_error_code(),
error.to_string(),
vec![],
vec![],
<_>::default(),
soft_limits_triggering,
)
}
fn signing_error_into_outcome(error: SigningError) -> InterpreterOutcome {
InterpreterOutcome::new(error.to_error_code(), error.to_string(), vec![], vec![], <_>::default())
fn signing_error_into_outcome(error: SigningError, soft_limits_triggering: SoftLimitsTriggering) -> InterpreterOutcome {
InterpreterOutcome::new(
error.to_error_code(),
error.to_string(),
vec![],
vec![],
<_>::default(),
soft_limits_triggering,
)
}
/// Deduplicate values in a supplied vector.

View File

@ -128,7 +128,7 @@ impl PreparationError {
}
pub fn air_size_limit(actual_size: usize, limit: u64) -> Self {
Self::SizeLimitsExceded(SizeLimitsExceded::AIR(actual_size, limit))
Self::SizeLimitsExceded(SizeLimitsExceded::Air(actual_size, limit))
}
pub fn particle_size_limit(actual_size: usize, limit: u64) -> Self {
@ -144,7 +144,7 @@ impl PreparationError {
pub enum SizeLimitsExceded {
/// AIR script size is bigger than the allowed limit.
#[error("air size: {0} bytes is bigger than the limit allowed: {1} bytes")]
AIR(usize, u64),
Air(usize, u64),
/// Current_data particle size is bigger than the allowed limit.
#[error("Current_data particle size: {0} bytes is bigger than the limit allowed: {1} bytes")]

View File

@ -26,6 +26,7 @@ use air_interpreter_data::Versions;
use air_interpreter_interface::CallResultsRepr;
use air_interpreter_interface::RunParameters;
use air_interpreter_interface::SerializedCallResults;
use air_interpreter_interface::SoftLimitsTriggering;
use air_interpreter_sede::FromSerialized;
use air_interpreter_signatures::KeyError;
use air_interpreter_signatures::KeyPair;
@ -75,6 +76,7 @@ pub(crate) fn prepare<'i>(
call_results: &SerializedCallResults,
run_parameters: RunParameters,
signature_store: SignatureStore,
soft_limits_triggering: &mut SoftLimitsTriggering,
) -> PreparationResult<PreparationDescriptor<'static, 'i>> {
let air: Instruction<'i> = air_parser::parse(raw_air).map_err(PreparationError::AIRParseError)?;
@ -94,6 +96,7 @@ pub(crate) fn prepare<'i>(
call_results,
signature_store,
&run_parameters,
soft_limits_triggering,
)?;
let trace_handler = TraceHandler::from_trace(prev_data.trace, current_data.trace);
@ -143,6 +146,7 @@ fn make_exec_ctx(
call_results: &SerializedCallResults,
signature_store: SignatureStore,
run_parameters: &RunParameters,
soft_limits_triggering: &mut SoftLimitsTriggering,
) -> PreparationResult<ExecutionCtx<'static>> {
use crate::preparation_step::sizes_limits_check::limit_behavior;
@ -159,8 +163,12 @@ fn make_exec_ctx(
.values()
.any(|call_result| call_result.result.len() > run_parameters.call_result_size_limit as usize)
{
let error = PreparationError::call_result_size_limit(run_parameters.call_result_size_limit);
limit_behavior(run_parameters, error)?;
let error: PreparationError = PreparationError::call_result_size_limit(run_parameters.call_result_size_limit);
limit_behavior(
run_parameters,
error,
&mut soft_limits_triggering.particle_size_limit_exceeded,
)?;
}
let ctx = ExecutionCtx::new(

View File

@ -17,12 +17,17 @@
use super::preparation::PreparationResult;
use crate::PreparationError;
use air_interpreter_interface::RunParameters;
use air_interpreter_interface::{RunParameters, SoftLimitsTriggering};
pub(crate) fn limit_behavior(run_parameters: &RunParameters, error: PreparationError) -> PreparationResult<()> {
pub(crate) fn limit_behavior(
run_parameters: &RunParameters,
error: PreparationError,
soft_limit_flag: &mut bool,
) -> PreparationResult<()> {
if run_parameters.hard_limit_enabled {
Err(error)
} else {
*soft_limit_flag = true;
Ok(())
}
}
@ -31,16 +36,26 @@ pub(crate) fn check_against_size_limits(
run_parameters: &RunParameters,
air: &str,
raw_current_data: &[u8],
) -> PreparationResult<()> {
) -> PreparationResult<SoftLimitsTriggering> {
let mut soft_limits_triggering = SoftLimitsTriggering::default();
if air.len() as u64 > run_parameters.air_size_limit {
let error = PreparationError::air_size_limit(air.len(), run_parameters.air_size_limit);
limit_behavior(run_parameters, error)?;
limit_behavior(
run_parameters,
error,
&mut soft_limits_triggering.air_size_limit_exceeded,
)?;
}
if raw_current_data.len() as u64 > run_parameters.particle_size_limit {
let error = PreparationError::particle_size_limit(raw_current_data.len(), run_parameters.particle_size_limit);
limit_behavior(run_parameters, error)?;
limit_behavior(
run_parameters,
error,
&mut soft_limits_triggering.particle_size_limit_exceeded,
)?;
}
Ok(())
Ok(soft_limits_triggering)
}

View File

@ -63,20 +63,34 @@ fn execute_air_impl(
) -> Result<InterpreterOutcome, InterpreterOutcome> {
use crate::preparation_step::check_against_size_limits;
farewell_if_fail!(
let mut soft_limits_triggering = farewell_if_fail!(
check_against_size_limits(&params, &air, &raw_current_data),
raw_prev_data
);
farewell_if_fail!(
check_against_size_limits(&params, &air, &raw_current_data),
raw_prev_data,
soft_limits_triggering
);
let ParsedDataPair {
prev_data,
current_data,
} = farewell_if_fail!(parse_data(&raw_prev_data, &raw_current_data), raw_prev_data);
} = farewell_if_fail!(
parse_data(&raw_prev_data, &raw_current_data),
raw_prev_data,
soft_limits_triggering
);
// TODO currently we use particle ID, but it should be changed to signature,
// as partical ID can be equally replayed
let salt = params.particle_id.clone();
let signature_store = farewell_if_fail!(verify(&prev_data, &current_data, &salt), raw_prev_data);
let signature_store = farewell_if_fail!(
verify(&prev_data, &current_data, &salt),
raw_prev_data,
soft_limits_triggering
);
let PreparationDescriptor {
mut exec_ctx,
@ -84,8 +98,17 @@ fn execute_air_impl(
air,
keypair,
} = farewell_if_fail!(
prepare(prev_data, current_data, &air, &call_results, params, signature_store,),
raw_prev_data
prepare(
prev_data,
current_data,
&air,
&call_results,
params,
signature_store,
&mut soft_limits_triggering
),
raw_prev_data,
soft_limits_triggering
);
// match here is used instead of map_err, because the compiler can't determine that
@ -103,18 +126,29 @@ fn execute_air_impl(
&salt,
&keypair,
),
raw_prev_data
raw_prev_data,
soft_limits_triggering
);
measure!(
match exec_result {
Ok(_) => farewell::from_success_result(exec_ctx, trace_handler, &keypair),
Ok(_) => farewell::from_success_result(exec_ctx, trace_handler, &keypair, soft_limits_triggering),
// return new collected trace in case of errors
Err(error) if error.is_catchable() => {
Err(farewell::from_execution_error(exec_ctx, trace_handler, error, &keypair))
Err(farewell::from_execution_error(
exec_ctx,
trace_handler,
error,
&keypair,
soft_limits_triggering,
))
}
// return the prev data in case of any trace errors
Err(error) => Err(farewell::from_uncatchable_error(raw_prev_data, error)),
Err(error) => Err(farewell::from_uncatchable_error(
raw_prev_data,
error,
soft_limits_triggering
)),
},
tracing::Level::INFO,
"farewell",

View File

@ -17,6 +17,7 @@
use super::CallRequests;
use crate::raw_outcome::RawAVMOutcome;
use air_interpreter_interface::SoftLimitsTriggering;
use serde::Deserialize;
use serde::Serialize;
@ -40,6 +41,8 @@ pub struct AVMOutcome {
/// Time of a particle execution
/// (it counts only execution time without operations with DataStore and so on)
pub execution_time: Duration,
soft_limits_triggering: SoftLimitsTriggering, // WIP
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -56,6 +59,7 @@ impl AVMOutcome {
next_peer_pks: Vec<String>,
memory_delta: usize,
execution_time: Duration,
soft_limits_triggering: SoftLimitsTriggering,
) -> Self {
Self {
data,
@ -63,6 +67,7 @@ impl AVMOutcome {
next_peer_pks,
memory_delta,
execution_time,
soft_limits_triggering,
}
}
@ -80,6 +85,7 @@ impl AVMOutcome {
data,
call_requests,
next_peer_pks,
soft_limits_triggering,
} = raw_outcome;
let avm_outcome = AVMOutcome::new(
@ -88,6 +94,7 @@ impl AVMOutcome {
next_peer_pks,
memory_delta,
execution_time,
soft_limits_triggering,
);
if ret_code == INTERPRETER_SUCCESS {

View File

@ -20,6 +20,7 @@ use super::CallRequests;
use air_interpreter_interface::InterpreterOutcome;
use air_interpreter_interface::SoftLimitsTriggering;
use serde::Deserialize;
use serde::Serialize;
@ -31,6 +32,7 @@ pub struct RawAVMOutcome {
pub data: Vec<u8>,
pub call_requests: CallRequests,
pub next_peer_pks: Vec<String>,
pub soft_limits_triggering: SoftLimitsTriggering,
}
impl RawAVMOutcome {
@ -41,9 +43,17 @@ impl RawAVMOutcome {
data,
call_requests,
next_peer_pks,
air_size_limit_exceeded,
particle_size_limit_exceeded,
call_result_size_limit_exceeded,
} = outcome;
let call_requests = crate::from_raw_call_requests(call_requests.into())?;
let soft_limits_triggering = SoftLimitsTriggering::new(
air_size_limit_exceeded,
particle_size_limit_exceeded,
call_result_size_limit_exceeded,
);
let raw_avm_outcome = Self {
ret_code,
@ -51,6 +61,7 @@ impl RawAVMOutcome {
data,
call_requests,
next_peer_pks,
soft_limits_triggering,
};
Ok(raw_avm_outcome)

View File

@ -43,6 +43,7 @@ pub struct AVMRuntimeLimits {
pub hard_limit_enabled: bool,
}
#[derive(Default)]
pub struct RuntimeLimits {
// The AIR script size limit.
pub air_size_limit: Option<u64>,
@ -392,17 +393,6 @@ impl RuntimeLimits {
}
}
impl Default for RuntimeLimits {
fn default() -> Self {
Self {
air_size_limit: None,
particle_size_limit: None,
call_result_size_limit: None,
hard_limit_enabled: false,
}
}
}
impl From<RuntimeLimits> for AVMRuntimeLimits {
fn from(value: RuntimeLimits) -> Self {
use air_interpreter_interface::MAX_AIR_SIZE;

View File

@ -25,6 +25,7 @@ pub struct AVMRuntimeLimits {
pub hard_limit_enabled: bool,
}
#[derive(Default)]
pub struct RuntimeLimits {
pub air_size_limit: Option<u64>,
pub particle_size_limit: Option<u64>,
@ -48,17 +49,6 @@ impl AVMRuntimeLimits {
}
}
impl Default for RuntimeLimits {
fn default() -> Self {
Self {
air_size_limit: None,
particle_size_limit: None,
call_result_size_limit: None,
hard_limit_enabled: false,
}
}
}
impl From<RuntimeLimits> for AVMRuntimeLimits {
fn from(value: RuntimeLimits) -> Self {
use air_interpreter_interface::MAX_AIR_SIZE;

View File

@ -24,6 +24,13 @@ use serde::Serialize;
pub const INTERPRETER_SUCCESS: i64 = 0;
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SoftLimitsTriggering {
pub air_size_limit_exceeded: bool,
pub particle_size_limit_exceeded: bool,
pub call_result_size_limit_exceeded: bool,
}
/// Describes a result returned at the end of the interpreter execution_step.
#[cfg_attr(feature = "marine", marine)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -43,6 +50,25 @@ pub struct InterpreterOutcome {
/// Collected parameters of all met call instructions that could be executed on a current peer.
pub call_requests: Vec<u8>,
/// WIP
pub air_size_limit_exceeded: bool,
pub particle_size_limit_exceeded: bool,
pub call_result_size_limit_exceeded: bool,
}
impl SoftLimitsTriggering {
pub fn new(
air_size_limit_exceeded: bool,
particle_size_limit_exceeded: bool,
call_result_size_limit_exceeded: bool,
) -> Self {
Self {
air_size_limit_exceeded,
particle_size_limit_exceeded,
call_result_size_limit_exceeded,
}
}
}
impl InterpreterOutcome {
@ -52,6 +78,7 @@ impl InterpreterOutcome {
data: Vec<u8>,
next_peer_pks: Vec<String>,
call_requests: SerializedCallRequests,
soft_limits_triggering: SoftLimitsTriggering,
) -> Self {
let call_requests = call_requests.into();
Self {
@ -60,6 +87,9 @@ impl InterpreterOutcome {
data,
next_peer_pks,
call_requests,
air_size_limit_exceeded: soft_limits_triggering.air_size_limit_exceeded,
particle_size_limit_exceeded: soft_limits_triggering.particle_size_limit_exceeded,
call_result_size_limit_exceeded: soft_limits_triggering.call_result_size_limit_exceeded,
}
}
}
@ -67,7 +97,7 @@ impl InterpreterOutcome {
#[cfg(feature = "marine")]
impl InterpreterOutcome {
pub fn from_ivalue(ivalue: IValue) -> Result<Self, String> {
const OUTCOME_FIELDS_COUNT: usize = 5;
const OUTCOME_FIELDS_COUNT: usize = 8;
let mut record_values = try_as_record(ivalue)?.into_vec();
if record_values.len() != OUTCOME_FIELDS_COUNT {
@ -76,11 +106,24 @@ impl InterpreterOutcome {
));
}
let air_size_limit_exceeded =
try_as_boolean(record_values.pop().unwrap(), "air_size_limit_exceeded")?;
let particle_size_limit_exceeded =
try_as_boolean(record_values.pop().unwrap(), "particle_size_limit_exceeded")?;
let call_result_size_limit_exceeded = try_as_boolean(
record_values.pop().unwrap(),
"call_result_size_limit_exceeded",
)?;
let call_requests = try_as_byte_vec(record_values.pop().unwrap(), "call_requests")?;
let next_peer_pks = try_as_string_vec(record_values.pop().unwrap(), "next_peer_pks")?;
let data = try_as_byte_vec(record_values.pop().unwrap(), "data")?;
let error_message = try_as_string(record_values.pop().unwrap(), "error_message")?;
let ret_code = try_as_i64(record_values.pop().unwrap(), "ret_code")?;
let soft_limits_triggering = SoftLimitsTriggering::new(
air_size_limit_exceeded,
particle_size_limit_exceeded,
call_result_size_limit_exceeded,
);
let outcome = Self::new(
ret_code,
@ -88,6 +131,7 @@ impl InterpreterOutcome {
data,
next_peer_pks,
call_requests.into(),
soft_limits_triggering,
);
Ok(outcome)
@ -160,3 +204,11 @@ fn try_as_string_vec(ivalue: IValue, field_name: &str) -> Result<Vec<String>, St
v => Err(format!("expected an array for {field_name}, got {v:?}")),
}
}
#[cfg(feature = "marine")]
fn try_as_boolean(ivalue: IValue, field_name: &str) -> Result<bool, String> {
match ivalue {
IValue::Boolean(value) => Ok(value),
v => Err(format!("expected a bool for {field_name}, got {v:?}")),
}
}

View File

@ -57,7 +57,7 @@ impl AirRunner for WasmAirRunner {
)
});
let runner = pool.pull(|| make_pooled_avm_runner(test_init_parameters.clone()));
let runner = pool.pull(|| make_pooled_avm_runner(test_init_parameters));
Self {
current_peer_id: current_peer_id.into(),

View File

@ -51,11 +51,26 @@ macro_rules! auto_checked_add {
#[macro_export]
macro_rules! farewell_if_fail {
($cmd:expr, $raw_prev_data:expr, $soft_limits_triggering:expr) => {
match $cmd {
Ok(result) => result,
// return the prev data in case of errors
Err(error) => {
return Err(farewell::from_uncatchable_error(
$raw_prev_data,
error,
$soft_limits_triggering,
))
}
};
};
($cmd:expr, $raw_prev_data:expr) => {
match $cmd {
Ok(result) => result,
// return the prev data in case of errors
Err(error) => return Err(farewell::from_uncatchable_error($raw_prev_data, error)),
Err(error) => {
farewell_if_fail!($cmd, $raw_prev_data, <_>::default())
}
};
};
}

View File

@ -164,7 +164,7 @@ impl<R: AirRunner> Network<R> {
resolver: Default::default(),
});
for peer_name in named_peers {
network.ensure_named_peer(peer_name, test_init_params.clone());
network.ensure_named_peer(peer_name, test_init_params);
}
network
}

View File

@ -100,7 +100,7 @@ impl<R: AirRunner> AirScriptExecutor<R> {
annotated_air_script: &str,
) -> Result<Self, String> {
let transformed =
TransformedAirScript::new(annotated_air_script, network, test_init_parameters.clone())?;
TransformedAirScript::new(annotated_air_script, network, test_init_parameters)?;
Self::from_transformed_air_script(test_parameters, test_init_parameters, transformed)
}
@ -115,10 +115,10 @@ impl<R: AirRunner> AirScriptExecutor<R> {
let network = Network::new(
extra_peers.into_iter(),
common_services,
test_init_parameters.clone(),
test_init_parameters,
);
let transformed =
TransformedAirScript::new(annotated_air_script, network, test_init_parameters.clone())?;
TransformedAirScript::new(annotated_air_script, network, test_init_parameters)?;
Self::from_transformed_air_script(test_parameters, test_init_parameters, transformed)
}

View File

@ -83,11 +83,11 @@ struct Transformer<'net, R> {
impl<R: AirRunner> Transformer<'_, R> {
pub(crate) fn transform(&self, sexp: &mut Sexp, test_init_parameters: TestInitParameters) {
match sexp {
Sexp::Call(call) => self.handle_call(call, test_init_parameters.clone()),
Sexp::Canon(canon) => self.handle_canon(canon, test_init_parameters.clone()),
Sexp::Call(call) => self.handle_call(call, test_init_parameters),
Sexp::Canon(canon) => self.handle_canon(canon, test_init_parameters),
Sexp::List(children) => {
for child in children.iter_mut().skip(1) {
self.transform(child, test_init_parameters.clone());
self.transform(child, test_init_parameters);
}
}
Sexp::Symbol(_) | Sexp::String(_) => {}