Fix Par state serialization in xor with inner par (#19)

This commit is contained in:
vms 2020-11-09 13:37:52 +03:00 committed by GitHub
parent 19ff54e66e
commit bcb0f18e9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 187 additions and 75 deletions

59
Cargo.lock generated
View File

@ -67,7 +67,7 @@ dependencies = [
[[package]] [[package]]
name = "aquamarine-vm" name = "aquamarine-vm"
version = "0.1.2" version = "0.1.2"
source = "git+https://github.com/fluencelabs/fce#c8114d48c6de55fea75f04c6998df7c685e8ce7b" source = "git+https://github.com/fluencelabs/fce#ddd3448af7b63017f68205c62ec7591888499a70"
dependencies = [ dependencies = [
"fluence-faas", "fluence-faas",
"maplit", "maplit",
@ -638,10 +638,11 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]] [[package]]
name = "fce" name = "fce"
version = "0.1.9" version = "0.1.10"
source = "git+https://github.com/fluencelabs/fce#c8114d48c6de55fea75f04c6998df7c685e8ce7b" source = "git+https://github.com/fluencelabs/fce#ddd3448af7b63017f68205c62ec7591888499a70"
dependencies = [ dependencies = [
"boolinator", "boolinator",
"fce-utils",
"fce-wit-interfaces", "fce-wit-interfaces",
"fce-wit-parser", "fce-wit-parser",
"log", "log",
@ -657,10 +658,15 @@ dependencies = [
"wasmer-wasi-fl", "wasmer-wasi-fl",
] ]
[[package]]
name = "fce-utils"
version = "0.1.0"
source = "git+https://github.com/fluencelabs/fce#ddd3448af7b63017f68205c62ec7591888499a70"
[[package]] [[package]]
name = "fce-wit-interfaces" name = "fce-wit-interfaces"
version = "0.1.6" version = "0.1.7"
source = "git+https://github.com/fluencelabs/fce#c8114d48c6de55fea75f04c6998df7c685e8ce7b" source = "git+https://github.com/fluencelabs/fce#ddd3448af7b63017f68205c62ec7591888499a70"
dependencies = [ dependencies = [
"multimap", "multimap",
"wasmer-interface-types-fl", "wasmer-interface-types-fl",
@ -668,8 +674,8 @@ dependencies = [
[[package]] [[package]]
name = "fce-wit-parser" name = "fce-wit-parser"
version = "0.1.8" version = "0.1.9"
source = "git+https://github.com/fluencelabs/fce#c8114d48c6de55fea75f04c6998df7c685e8ce7b" source = "git+https://github.com/fluencelabs/fce#ddd3448af7b63017f68205c62ec7591888499a70"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"fce-wit-interfaces", "fce-wit-interfaces",
@ -695,11 +701,12 @@ dependencies = [
[[package]] [[package]]
name = "fluence-faas" name = "fluence-faas"
version = "0.1.10" version = "0.1.11"
source = "git+https://github.com/fluencelabs/fce#c8114d48c6de55fea75f04c6998df7c685e8ce7b" source = "git+https://github.com/fluencelabs/fce#ddd3448af7b63017f68205c62ec7591888499a70"
dependencies = [ dependencies = [
"cmd_lib", "cmd_lib",
"fce", "fce",
"fce-utils",
"fluence-sdk-main 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "fluence-sdk-main 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools", "itertools",
"log", "log",
@ -714,6 +721,14 @@ dependencies = [
"wasmer-wasi-fl", "wasmer-wasi-fl",
] ]
[[package]]
name = "fluence-sdk-macro"
version = "0.2.8"
source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30"
dependencies = [
"fluence-sdk-wit 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)",
]
[[package]] [[package]]
name = "fluence-sdk-macro" name = "fluence-sdk-macro"
version = "0.2.8" version = "0.2.8"
@ -724,11 +739,13 @@ dependencies = [
] ]
[[package]] [[package]]
name = "fluence-sdk-macro" name = "fluence-sdk-main"
version = "0.2.8" version = "0.2.8"
source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30" source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30"
dependencies = [ dependencies = [
"fluence-sdk-wit 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)", "fluence-sdk-macro 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)",
"log",
"serde",
] ]
[[package]] [[package]]
@ -742,21 +759,10 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "fluence-sdk-main"
version = "0.2.8"
source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30"
dependencies = [
"fluence-sdk-macro 0.2.8 (git+https://github.com/fluencelabs/rust-sdk)",
"log",
"serde",
]
[[package]] [[package]]
name = "fluence-sdk-wit" name = "fluence-sdk-wit"
version = "0.2.8" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30"
checksum = "560baf91197ded38a99a5c94ff366a3dd971ebf33f5d987ecce31d3dedf86d17"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -769,7 +775,8 @@ dependencies = [
[[package]] [[package]]
name = "fluence-sdk-wit" name = "fluence-sdk-wit"
version = "0.2.8" version = "0.2.8"
source = "git+https://github.com/fluencelabs/rust-sdk#a6c587db0b6f22c3d3af81f10b187f148f8e9d30" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "560baf91197ded38a99a5c94ff366a3dd971ebf33f5d987ecce31d3dedf86d17"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1981,9 +1988,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmer-interface-types-fl" name = "wasmer-interface-types-fl"
version = "0.17.10" version = "0.17.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de496e366bd1c198942248fc1de4b94e4647b263dd60099d5f7776f0d621656" checksum = "e916b92f2d315ea27d5ff1d3d6410fe852c51d21bb91a8d1ba7adbf701de7f53"
dependencies = [ dependencies = [
"log", "log",
"nom", "nom",

View File

@ -108,13 +108,13 @@ impl<'i> ParsedCall<'i> {
use crate::call_evidence::CallResult::*; use crate::call_evidence::CallResult::*;
use crate::call_evidence::EvidenceState::*; use crate::call_evidence::EvidenceState::*;
if call_ctx.current_subtree_elements_count == 0 { if call_ctx.current_subtree_size == 0 {
log::info!(target: EVIDENCE_CHANGING, " previous call evidence state wasn't found"); log::info!(target: EVIDENCE_CHANGING, " previous call evidence state wasn't found");
return Ok(true); return Ok(true);
} }
call_ctx.current_subtree_elements_count -= 1; call_ctx.current_subtree_size -= 1;
// unwrap is safe here, because current_subtree_elements_count depends on current_path len, // unwrap is safe here, because current_subtree_size depends on current_path len,
// and it's been checked previously // and it's been checked previously
let prev_state = call_ctx.current_path.pop_front().unwrap(); let prev_state = call_ctx.current_path.pop_front().unwrap();

View File

@ -39,12 +39,12 @@ pub(crate) trait ExecutableInstruction<'i> {
impl<'i> ExecutableInstruction<'i> for Instruction<'i> { impl<'i> ExecutableInstruction<'i> for Instruction<'i> {
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, call_ctx: &mut CallEvidenceCtx) -> Result<()> { fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, call_ctx: &mut CallEvidenceCtx) -> Result<()> {
match self { match self {
Instruction::Seq(seq) => seq.execute(exec_ctx, call_ctx),
Instruction::Call(call) => call.execute(exec_ctx, call_ctx), Instruction::Call(call) => call.execute(exec_ctx, call_ctx),
Instruction::Null(null) => null.execute(exec_ctx, call_ctx),
Instruction::Fold(fold) => fold.execute(exec_ctx, call_ctx), Instruction::Fold(fold) => fold.execute(exec_ctx, call_ctx),
Instruction::Next(next) => next.execute(exec_ctx, call_ctx), Instruction::Next(next) => next.execute(exec_ctx, call_ctx),
Instruction::Null(null) => null.execute(exec_ctx, call_ctx),
Instruction::Par(par) => par.execute(exec_ctx, call_ctx), Instruction::Par(par) => par.execute(exec_ctx, call_ctx),
Instruction::Seq(seq) => seq.execute(exec_ctx, call_ctx),
Instruction::Xor(xor) => xor.execute(exec_ctx, call_ctx), Instruction::Xor(xor) => xor.execute(exec_ctx, call_ctx),
Instruction::Error => unreachable!("should not execute if parsing failed. QED."), Instruction::Error => unreachable!("should not execute if parsing failed. QED."),
} }
@ -84,7 +84,7 @@ macro_rules! log_instruction {
log::info!( log::info!(
target: crate::log_targets::SUBTREE_ELEMENTS, target: crate::log_targets::SUBTREE_ELEMENTS,
" subtree elements count: {:?}", " subtree elements count: {:?}",
$call_ctx.current_subtree_elements_count $call_ctx.current_subtree_size
); );
log::info!( log::info!(
target: crate::log_targets::NEW_CALL_EVIDENCE_PATH, target: crate::log_targets::NEW_CALL_EVIDENCE_PATH,

View File

@ -25,37 +25,49 @@ use crate::Result;
use air_parser::ast::Par; use air_parser::ast::Par;
enum SubtreeType {
Left,
Right,
}
impl std::fmt::Display for SubtreeType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Left => write!(f, "left"),
Self::Right => write!(f, "right"),
}
}
}
impl<'i> ExecutableInstruction<'i> for Par<'i> { impl<'i> ExecutableInstruction<'i> for Par<'i> {
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, call_ctx: &mut CallEvidenceCtx) -> Result<()> { fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, call_ctx: &mut CallEvidenceCtx) -> Result<()> {
use SubtreeType::*;
log_instruction!(par, exec_ctx, call_ctx); log_instruction!(par, exec_ctx, call_ctx);
let (left_subtree_size, right_subtree_size) = extract_subtree_sizes(call_ctx)?; let (left_subtree_size, right_subtree_size) = extract_subtree_sizes(call_ctx)?;
let pre_states_count = call_ctx.current_path.len(); let before_path_size = call_ctx.current_path.len();
let pre_unused_elements = call_ctx.current_subtree_elements_count; let before_subtree_size = call_ctx.current_subtree_size;
let pre_new_states_count = call_ctx.new_path.len(); let par_pos = call_ctx.new_path.len();
call_ctx.new_path.push_back(EvidenceState::Par(0, 0)); call_ctx.new_path.push_back(EvidenceState::Par(0, 0));
let new_left_subtree_size = execute_subtree(&self.0, left_subtree_size, exec_ctx, call_ctx)?; // execute a left subtree of this par
execute_subtree(&self.0, left_subtree_size, exec_ctx, call_ctx, par_pos, Left)?;
let left_subtree_complete = exec_ctx.subtree_complete; let left_subtree_complete = exec_ctx.subtree_complete;
let new_right_subtree_size = execute_subtree(&self.1, right_subtree_size, exec_ctx, call_ctx)?; // execute a right subtree of this par
execute_subtree(&self.1, right_subtree_size, exec_ctx, call_ctx, par_pos, Right)?;
let right_subtree_complete = exec_ctx.subtree_complete; let right_subtree_complete = exec_ctx.subtree_complete;
// par is completed if at least one of its subtrees is completed // par is completed if at least one of its subtrees is completed
exec_ctx.subtree_complete = left_subtree_complete || right_subtree_complete; exec_ctx.subtree_complete = left_subtree_complete || right_subtree_complete;
let new_par_evidence_state = EvidenceState::Par(new_left_subtree_size, new_right_subtree_size); // decrease current subtree size by used elements from current_path
log::info!( let after_path_size = call_ctx.current_path.len();
target: EVIDENCE_CHANGING, let used_path_elements = before_path_size - after_path_size;
" adding new call evidence state {:?}", call_ctx.current_subtree_size = before_subtree_size - used_path_elements;
new_par_evidence_state
);
call_ctx.new_path[pre_new_states_count] = new_par_evidence_state;
let post_states_count = call_ctx.current_path.len();
call_ctx.current_subtree_elements_count = pre_unused_elements - (pre_states_count - post_states_count);
Ok(()) Ok(())
} }
@ -64,11 +76,10 @@ impl<'i> ExecutableInstruction<'i> for Par<'i> {
fn extract_subtree_sizes(call_ctx: &mut CallEvidenceCtx) -> Result<(usize, usize)> { fn extract_subtree_sizes(call_ctx: &mut CallEvidenceCtx) -> Result<(usize, usize)> {
use crate::AquamarineError::InvalidEvidenceState; use crate::AquamarineError::InvalidEvidenceState;
if call_ctx.current_subtree_elements_count == 0 { if call_ctx.current_subtree_size == 0 {
return Ok((0, 0)); return Ok((0, 0));
} }
call_ctx.current_subtree_size -= 1;
call_ctx.current_subtree_elements_count -= 1;
log::info!( log::info!(
target: EVIDENCE_CHANGING, target: EVIDENCE_CHANGING,
@ -83,21 +94,36 @@ fn extract_subtree_sizes(call_ctx: &mut CallEvidenceCtx) -> Result<(usize, usize
} }
} }
/// Execute provided subtree and update Par state in call_ctx.new_path.
fn execute_subtree<'i>( fn execute_subtree<'i>(
subtree: &Instruction<'i>, subtree: &Instruction<'i>,
subtree_size: usize, subtree_size: usize,
exec_ctx: &mut ExecutionCtx<'i>, exec_ctx: &mut ExecutionCtx<'i>,
call_ctx: &mut CallEvidenceCtx, call_ctx: &mut CallEvidenceCtx,
) -> Result<usize> { current_par_pos: usize,
call_ctx.current_subtree_elements_count = subtree_size; subtree_type: SubtreeType,
let before_states_count = call_ctx.new_path.len(); ) -> Result<()> {
use crate::AquamarineError::LocalServiceError;
call_ctx.current_subtree_size = subtree_size;
let before_new_path_len = call_ctx.new_path.len();
exec_ctx.subtree_complete = determine_subtree_complete(&subtree); exec_ctx.subtree_complete = determine_subtree_complete(&subtree);
// execute subtree // execute subtree
subtree.execute(exec_ctx, call_ctx)?; match subtree.execute(exec_ctx, call_ctx) {
res @ Ok(_) => {
Ok(call_ctx.new_path.len() - before_states_count) update_par_state(call_ctx, subtree_type, current_par_pos, before_new_path_len);
res
}
// if there is a service error, update already added Par state
// and then bubble the error up
err @ Err(LocalServiceError(_)) => {
update_par_state(call_ctx, subtree_type, current_par_pos, before_new_path_len);
err
}
err @ Err(_) => err,
}
} }
fn determine_subtree_complete(next_instruction: &Instruction<'_>) -> bool { fn determine_subtree_complete(next_instruction: &Instruction<'_>) -> bool {
@ -108,10 +134,40 @@ fn determine_subtree_complete(next_instruction: &Instruction<'_>) -> bool {
// (next i) // (next i)
// ) // )
// ) // )
// par will be executed after the last next that wouldn't change subtree_complete // par will be completed after the last next that wouldn't change subtree_complete
!matches!(next_instruction, Instruction::Next(_)) !matches!(next_instruction, Instruction::Next(_))
} }
/// Set left or right fields of a Par identified by current_par_pos.
fn update_par_state(
call_ctx: &mut CallEvidenceCtx,
subtree_type: SubtreeType,
current_par_pos: usize,
before_new_path_len: usize,
) {
let new_subtree_size = call_ctx.new_path.len() - before_new_path_len;
// unwrap is safe here, because this par is added at the beginning of this par instruction.
let par_state = call_ctx.new_path.get_mut(current_par_pos).unwrap();
match par_state {
EvidenceState::Par(left, right) => {
if let SubtreeType::Left = subtree_type {
*left = new_subtree_size;
} else {
*right = new_subtree_size;
}
log::info!(
target: EVIDENCE_CHANGING,
" set {} par subtree size to {}",
subtree_type,
new_subtree_size
);
}
_ => unreachable!("current_pas_pos must point to a par state"),
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use aqua_test_utils::call_vm; use aqua_test_utils::call_vm;

View File

@ -50,19 +50,15 @@ mod tests {
use std::rc::Rc; use std::rc::Rc;
#[test] fn fallible_call_service(fallible_service_id: String) -> HostExportedFunc {
fn xor() { Box::new(move |_, args| -> Option<IValue> {
use crate::call_evidence::CallResult::*;
use crate::call_evidence::EvidenceState::*;
let call_service: HostExportedFunc = Box::new(|_, args| -> Option<IValue> {
let builtin_service = match &args[0] { let builtin_service = match &args[0] {
IValue::String(str) => str, IValue::String(str) => str,
_ => unreachable!(), _ => unreachable!(),
}; };
if builtin_service == "service_id_1" { // return a error for service with such id
// return a error for service with id service_id_1 if builtin_service == &fallible_service_id {
Some(IValue::Record( Some(IValue::Record(
Vec1::new(vec![IValue::S32(1), IValue::String(String::from(r#""error""#))]).unwrap(), Vec1::new(vec![IValue::S32(1), IValue::String(String::from(r#""error""#))]).unwrap(),
)) ))
@ -72,9 +68,16 @@ mod tests {
Vec1::new(vec![IValue::S32(0), IValue::String(String::from(r#""res""#))]).unwrap(), Vec1::new(vec![IValue::S32(0), IValue::String(String::from(r#""res""#))]).unwrap(),
)) ))
} }
}); })
}
let mut vm = create_aqua_vm(call_service, ""); #[test]
fn xor() {
use crate::call_evidence::CallResult::*;
use crate::call_evidence::EvidenceState::*;
let fallible_service_id = String::from("service_id_1");
let mut vm = create_aqua_vm(fallible_call_service(fallible_service_id), "");
let script = String::from( let script = String::from(
r#" r#"
@ -111,4 +114,54 @@ mod tests {
Call(Executed(Rc::new(JValue::String(String::from("res"))))) Call(Executed(Rc::new(JValue::String(String::from("res")))))
); );
} }
#[test]
fn xor_par() {
use crate::call_evidence::CallResult::*;
use crate::call_evidence::EvidenceState::*;
let fallible_service_id = String::from("service_id_1");
let mut vm = create_aqua_vm(fallible_call_service(fallible_service_id), "");
let script = String::from(
r#"
(xor
(par
(seq
(call %current_peer_id% ("service_id_2" "local_fn_name") [] result_1)
(call %current_peer_id% ("service_id_2" "local_fn_name") [] result_2)
)
(par
(call %current_peer_id% ("service_id_1" "local_fn_name") [] result_3)
(call %current_peer_id% ("service_id_2" "local_fn_name") [] result_4)
)
)
(seq
(call %current_peer_id% ("service_id_2" "local_fn_name") [] result_4)
(call %current_peer_id% ("service_id_2" "local_fn_name") [] result_5)
)
)"#,
);
let result = call_vm!(vm, "asd", script.clone(), "[]", "[]");
let result_path: CallEvidencePath = serde_json::from_str(&result.data).expect("should be valid json");
let res = String::from("res");
let right_path = vec![
Par(2, 2),
Call(Executed(Rc::new(JValue::String(res.clone())))),
Call(Executed(Rc::new(JValue::String(res.clone())))),
Par(1, 0),
Call(CallServiceFailed(String::from(r#""error""#))),
Call(Executed(Rc::new(JValue::String(res.clone())))),
Call(Executed(Rc::new(JValue::String(res)))),
];
assert_eq!(result_path, right_path);
let result = call_vm!(vm, "asd", script, "[]", result.data);
let result_path: CallEvidencePath = serde_json::from_str(&result.data).expect("should be valid json");
assert_eq!(result_path, right_path);
}
} }

View File

@ -24,17 +24,17 @@ use std::fmt::Formatter;
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub(crate) struct CallEvidenceCtx { pub(crate) struct CallEvidenceCtx {
pub(crate) current_path: CallEvidencePath, pub(crate) current_path: CallEvidencePath,
pub(crate) current_subtree_elements_count: usize, pub(crate) current_subtree_size: usize,
// TODO: consider change it to Vec for optimization // TODO: consider change it to Vec for optimization
pub(crate) new_path: CallEvidencePath, pub(crate) new_path: CallEvidencePath,
} }
impl CallEvidenceCtx { impl CallEvidenceCtx {
pub fn new(current_path: CallEvidencePath) -> Self { pub fn new(current_path: CallEvidencePath) -> Self {
let current_subtree_elements_count = current_path.len(); let current_subtree_size = current_path.len();
Self { Self {
current_path, current_path,
current_subtree_elements_count, current_subtree_size,
new_path: CallEvidencePath::new(), new_path: CallEvidencePath::new(),
} }
} }
@ -43,11 +43,7 @@ impl CallEvidenceCtx {
impl Display for CallEvidenceCtx { impl Display for CallEvidenceCtx {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "current path:\n{:?}", self.current_path)?; writeln!(f, "current path:\n{:?}", self.current_path)?;
writeln!( writeln!(f, "current subtree elements count:\n{:?}", self.current_subtree_size)?;
f,
"current subtree elements count:\n{:?}",
self.current_subtree_elements_count
)?;
writeln!(f, "new path:\n{:?}", self.new_path) writeln!(f, "new path:\n{:?}", self.new_path)
} }
} }