feat(interface)!: Use MessagePack for calls (#780)

Top-level structs use multicodec-prefixed MessagePack, the nested
structures (arguments, tetraplets) use just ordinary MessagePack.

* JS-compatible `CallResults`

Binary format implementations like `rmp-serde` do not bother converting keys from strings, unlike `serde_json`.
So, we use `CallResults` with string keys, as JS client cannot produce anything else.

---------

Co-authored-by: Mike Voronov <michail.vms@gmail.com>
This commit is contained in:
Ivan Boldyrev 2024-01-08 15:01:58 +04:00 committed by GitHub
parent ccb8f025b7
commit 325eea7e91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1508 additions and 1498 deletions

View File

@ -80,7 +80,9 @@ pub(super) fn handle_prev_state<'i>(
RequestSentBy(Sender::PeerIdWithCallId { ref peer_id, call_id }) RequestSentBy(Sender::PeerIdWithCallId { ref peer_id, call_id })
if peer_id.as_str() == exec_ctx.run_parameters.current_peer_id.as_str() => if peer_id.as_str() == exec_ctx.run_parameters.current_peer_id.as_str() =>
{ {
// call results are identified by call_id that is saved in data // call results are identified by call_id that is saved in data;
// for compatiblity with JavaScript with binary formats, string IDs are used
let call_id = call_id.to_string();
match exec_ctx.call_results.remove(&call_id) { match exec_ctx.call_results.remove(&call_id) {
Some(call_result) => { Some(call_result) => {
update_state_with_service_result( update_state_with_service_result(

View File

@ -35,7 +35,7 @@ fn unprocessed_call_result() {
let expected_call_service_result = air_interpreter_interface::CallServiceResult::ok(&json!("null")); let expected_call_service_result = air_interpreter_interface::CallServiceResult::ok(&json!("null"));
let expected_call_results = maplit::hashmap!( let expected_call_results = maplit::hashmap!(
70 => expected_call_service_result, "70".to_owned() => expected_call_service_result,
); );
let expected_error = FarewellError::UnprocessedCallResult(expected_call_results); let expected_error = FarewellError::UnprocessedCallResult(expected_call_results);
assert!(check_error(&result, expected_error)); assert!(check_error(&result, expected_error));

View File

@ -15,16 +15,16 @@
*/ */
import { CallResultsArray, InterpreterResult, CallRequest, RunParameters, JSONArray, JSONObject } from './types'; import { CallResultsArray, InterpreterResult, CallRequest, RunParameters, JSONArray, JSONObject } from './types';
import { JsonRepr } from './formats' import { MulticodecRepr, MsgPackRepr } from './formats'
// Have to match the air-interpreter-interface. // Have to match the air-interpreter-interface.
const callRequestsRepr = new JsonRepr(); const callRequestsRepr = new MulticodecRepr(new MsgPackRepr());
// Have to match the air-interpreter-interface. // Have to match the air-interpreter-interface.
const argumentRepr = new JsonRepr(); const argumentRepr = new MsgPackRepr();
// Have to match the air-interpreter-interface. // Have to match the air-interpreter-interface.
const tetrapletRepr = new JsonRepr(); const tetrapletRepr = new MsgPackRepr();
// Have to match the air-interpreter-interface. // Have to match the air-interpreter-interface.
const callResultsRepr = new JsonRepr(); const callResultsRepr = new MulticodecRepr(new MsgPackRepr());
/** /**
* Encodes arguments into JSON array suitable for marine-js * Encodes arguments into JSON array suitable for marine-js

View File

@ -93,7 +93,7 @@ export class MulticodecRepr implements Representation {
toBinary(obj: object): Uint8Array { toBinary(obj: object): Uint8Array {
let bareData = this.serializer.toBinary(obj); let bareData = this.serializer.toBinary(obj);
let varintCode = multicodec.getVarintFromCode(this.serializer.get_code()); let codeName = multicodec.getNameFromCode(this.serializer.get_code());
return multicodec.addPrefix(varintCode, bareData) return multicodec.addPrefix(codeName, bareData)
} }
} }

View File

@ -63,7 +63,7 @@ impl CallServiceResult {
pub fn into_raw_result(call_results: CallResults) -> air_interpreter_interface::CallResults { pub fn into_raw_result(call_results: CallResults) -> air_interpreter_interface::CallResults {
call_results call_results
.into_iter() .into_iter()
.map(|(call_id, call_result)| (call_id, call_result.into_raw())) .map(|(call_id, call_result)| (call_id.to_string(), call_result.into_raw()))
.collect::<_>() .collect::<_>()
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@ path = "src/lib.rs"
[dependencies] [dependencies]
marine-rs-sdk = {version = "0.10.2", optional = true } marine-rs-sdk = {version = "0.10.2", optional = true }
fluence-it-types = { version = "0.4.1", optional = true } fluence-it-types = { version = "0.4.1", optional = true }
air-interpreter-sede = { version = "0.1.0", path = "../interpreter-sede", features = ["json"] } air-interpreter-sede = { version = "0.1.0", path = "../interpreter-sede", features = ["msgpack"] }
marine-call-parameters = { version = "0.10.3", default-features = false } marine-call-parameters = { version = "0.10.3", default-features = false }
serde = "1.0.190" serde = "1.0.190"

View File

@ -18,7 +18,8 @@ use air_interpreter_sede::define_simple_representation;
use air_interpreter_sede::derive_serialized_type; use air_interpreter_sede::derive_serialized_type;
use air_interpreter_sede::Format; use air_interpreter_sede::Format;
use air_interpreter_sede::FromSerialized; use air_interpreter_sede::FromSerialized;
use air_interpreter_sede::JsonFormat; use air_interpreter_sede::MsgPackFormat;
use air_interpreter_sede::MsgPackMultiformat;
use air_interpreter_sede::Representation; use air_interpreter_sede::Representation;
use marine_call_parameters::SecurityTetraplet; use marine_call_parameters::SecurityTetraplet;
@ -36,9 +37,9 @@ derive_serialized_type!(SerializedCallArguments);
derive_serialized_type!(SerializedTetraplets); derive_serialized_type!(SerializedTetraplets);
derive_serialized_type!(SerializedCallRequests); derive_serialized_type!(SerializedCallRequests);
pub type CallArgumentsFormat = JsonFormat; pub type CallArgumentsFormat = MsgPackFormat;
pub type TetrapletsFormat = JsonFormat; pub type TetrapletsFormat = MsgPackFormat;
pub type CallRequestsFormat = JsonFormat; pub type CallRequestsFormat = MsgPackMultiformat;
define_simple_representation! { define_simple_representation! {
CallArgumentsRepr, CallArgumentsRepr,
@ -74,7 +75,7 @@ impl FromSerialized<Vec<Vec<SecurityTetraplet>>> for TetrapletsRepr {
&self, &self,
repr: &[u8], repr: &[u8],
) -> Result<Vec<Vec<SecurityTetraplet>>, Self::DeserializeError> { ) -> Result<Vec<Vec<SecurityTetraplet>>, Self::DeserializeError> {
CallArgumentsRepr.get_format().from_slice(repr) Self.get_format().from_slice(repr)
} }
} }

View File

@ -16,17 +16,19 @@
use air_interpreter_sede::define_simple_representation; use air_interpreter_sede::define_simple_representation;
use air_interpreter_sede::derive_serialized_type; use air_interpreter_sede::derive_serialized_type;
use air_interpreter_sede::JsonFormat; use air_interpreter_sede::MsgPackMultiformat;
use air_interpreter_sede::Representation; use air_interpreter_sede::Representation;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use serde_json::Value as JValue; use serde_json::Value as JValue;
use std::collections::HashMap; use std::collections::HashMap;
pub type CallResults = HashMap<u32, CallServiceResult>; /// This is a map from a String to a service result for compatibility with JavaScript.
/// Binary format implementations like `rmp-serde` do not convert keys from strings, unlike `serde_json`.
pub type CallResults = HashMap<String, CallServiceResult>;
pub const CALL_SERVICE_SUCCESS: i32 = 0; pub const CALL_SERVICE_SUCCESS: i32 = 0;
pub type CallResultsFormat = JsonFormat; pub type CallResultsFormat = MsgPackMultiformat;
derive_serialized_type!(SerializedCallResults); derive_serialized_type!(SerializedCallResults);
@ -55,6 +57,7 @@ impl CallServiceResult {
pub fn ok(result: &JValue) -> Self { pub fn ok(result: &JValue) -> Self {
Self { Self {
ret_code: CALL_SERVICE_SUCCESS, ret_code: CALL_SERVICE_SUCCESS,
// for compatiblity with JavaScript with binary formats, string IDs are used
result: result.to_string(), result: result.to_string(),
} }
} }

View File

@ -35,7 +35,9 @@ where
#[inline] #[inline]
fn to_vec(&self, val: &Value) -> Result<Vec<u8>, Self::SerializationError> { fn to_vec(&self, val: &Value) -> Result<Vec<u8>, Self::SerializationError> {
rmp_serde::to_vec(val) // named representation (i.e. structs are serialized as maps, not tuples) is important
// for JS interop and data compatibility detection
rmp_serde::to_vec_named(val)
} }
#[inline] #[inline]
@ -49,7 +51,9 @@ where
value: &Value, value: &Value,
write: &mut W, write: &mut W,
) -> Result<(), Self::WriteError> { ) -> Result<(), Self::WriteError> {
value.serialize(&mut rmp_serde::Serializer::new(write)) // named representation (i.e. structs are serialized as maps, not tuples) is important
// for JS interop and data compatibility detection
rmp_serde::encode::write_named(write, value)
} }
} }