mirror of
https://github.com/fluencelabs/aquavm
synced 2025-06-24 20:21:34 +00:00
feat(interpreter-data)!: New data format for calls (#501)
BREAKING CHANGE: 1. Call values in the trace have CID references to structures that have call arguments' hash and CID references to values and tetraplets. 2. If call value is unused, it is serialized with `Unused` variant, and CID references are not stored. Previous data scheme was (Scalar as an example, other cases are similar): ``` Scalar(CID<JValue>) ---<value_store>----> JValue ``` New data scheme is much more sophisticated: ``` Scalar(CID<ServiceResultAggregate>) ---+ | +----<service_result_store>----------+ | +-------> ServiceResultAggregate: value_cid ------------<value_store>----> JValue tetraplet_cid --------<tetraplet_store>----> SecurityTetraplet argument_hash: String ``` `Stream` variant is similar, however, `Unused` is different: it has value CID only, but the value is not stored into the `value_store`: ``` Unused(Rc<CID<JValue>>) ---> X ``` Co-authored-by: Mike Voronov <michail.vms@gmail.com>
This commit is contained in:
@ -23,7 +23,6 @@ use crate::TracePos;
|
||||
use air_interpreter_cid::CID;
|
||||
use polyplets::SecurityTetraplet;
|
||||
use se_de::par_serializer;
|
||||
use se_de::sender_serializer;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
@ -36,7 +35,7 @@ pub struct ParResult {
|
||||
pub right_size: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Sender {
|
||||
PeerId(Rc<String>),
|
||||
PeerIdWithCallId { peer_id: Rc<String>, call_id: u32 },
|
||||
@ -46,26 +45,79 @@ pub enum Sender {
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CallResult {
|
||||
/// Request was sent to a target node by node with such public key and it shouldn't be called again.
|
||||
#[serde(with = "sender_serializer")]
|
||||
#[serde(rename = "sent_by")]
|
||||
RequestSentBy(Sender),
|
||||
|
||||
/// A corresponding call's been already executed with such value as a result.
|
||||
Executed(ValueRef),
|
||||
|
||||
/// call_service ended with a service error.
|
||||
#[serde(rename = "failed")]
|
||||
CallServiceFailed(i32, Rc<String>),
|
||||
/// The call returned a service error.
|
||||
///
|
||||
/// The `JValue` has to be a two element array `[i32, String]`.
|
||||
Failed(Rc<CID<ServiceResultAggregate>>),
|
||||
}
|
||||
|
||||
/*
|
||||
* The current value structure is:
|
||||
*
|
||||
* ```
|
||||
* Scalar(CID<ServiceResultAggregate>) ---+
|
||||
* |
|
||||
* +----<service_result_store>------+
|
||||
* |
|
||||
* +-------> ServiceResultAggregate:
|
||||
* value_cid ------------<value_store>----> JValue
|
||||
* tetraplet_cid --------<tetraplet_store>----> SecurityTetraplet
|
||||
* argument_hash: String
|
||||
* ```
|
||||
*
|
||||
* `Stream` variant is similar, however, `Unused` is different: it has value CID only, but the value
|
||||
* is not stored into the `value_store`:
|
||||
*
|
||||
* ```
|
||||
* Unused(Rc<CID<JValue>>) ---> X
|
||||
* ```
|
||||
*/
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ValueRef {
|
||||
/// The call value is stored to a scalar variable.
|
||||
Scalar(Rc<CID<ServiceResultAggregate>>),
|
||||
/// The call value is stored to a stream variable.
|
||||
Stream {
|
||||
cid: Rc<CID<ServiceResultAggregate>>,
|
||||
generation: u32,
|
||||
},
|
||||
/// The call value is not stored.
|
||||
Unused(Rc<CID<JValue>>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct CallServiceFailed {
|
||||
pub ret_code: i32,
|
||||
/// This field contains a JSON-serialized value, not a plain error message.
|
||||
pub message: Rc<String>,
|
||||
}
|
||||
|
||||
impl CallServiceFailed {
|
||||
pub fn new(ret_code: i32, message: Rc<String>) -> Self {
|
||||
Self { ret_code, message }
|
||||
}
|
||||
|
||||
pub fn to_value(&self) -> JValue {
|
||||
serde_json::to_value(self).expect("default serializer shouldn't fail")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ValueRef {
|
||||
Scalar(Rc<CID<JValue>>),
|
||||
Stream {
|
||||
cid: Rc<CID<JValue>>,
|
||||
generation: u32,
|
||||
},
|
||||
/// A proof of service result execution result.
|
||||
pub struct ServiceResultAggregate {
|
||||
pub value_cid: Rc<CID<JValue>>,
|
||||
/// Hash of the call arguments.
|
||||
pub argument_hash: Rc<str>,
|
||||
/// The tetraplet of the call result.
|
||||
pub tetraplet_cid: Rc<CID<SecurityTetraplet>>,
|
||||
}
|
||||
|
||||
/// Let's consider an example of trace that could be produces by the following fold:
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::JValue;
|
||||
|
||||
impl ParResult {
|
||||
pub fn new(left_size: u32, right_size: u32) -> Self {
|
||||
@ -42,20 +41,30 @@ impl CallResult {
|
||||
CallResult::RequestSentBy(Sender::PeerIdWithCallId { peer_id, call_id })
|
||||
}
|
||||
|
||||
pub fn executed_scalar(cid: Rc<CID<JValue>>) -> CallResult {
|
||||
let value = ValueRef::Scalar(cid);
|
||||
|
||||
CallResult::Executed(value)
|
||||
pub fn executed_service_result(value_ref: ValueRef) -> Self {
|
||||
Self::Executed(value_ref)
|
||||
}
|
||||
|
||||
pub fn executed_stream(cid: Rc<CID<JValue>>, generation: u32) -> CallResult {
|
||||
let value = ValueRef::Stream { cid, generation };
|
||||
|
||||
CallResult::Executed(value)
|
||||
pub fn executed_scalar(service_result_agg_cid: Rc<CID<ServiceResultAggregate>>) -> Self {
|
||||
Self::executed_service_result(ValueRef::Scalar(service_result_agg_cid))
|
||||
}
|
||||
|
||||
pub fn failed(ret_code: i32, error_msg: impl Into<String>) -> CallResult {
|
||||
CallResult::CallServiceFailed(ret_code, Rc::new(error_msg.into()))
|
||||
pub fn executed_stream(
|
||||
service_result_agg_cid: Rc<CID<ServiceResultAggregate>>,
|
||||
generation: u32,
|
||||
) -> CallResult {
|
||||
Self::executed_service_result(ValueRef::Stream {
|
||||
cid: service_result_agg_cid,
|
||||
generation,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn executed_unused(value_cid: Rc<CID<JValue>>) -> CallResult {
|
||||
Self::executed_service_result(ValueRef::Unused(value_cid))
|
||||
}
|
||||
|
||||
pub fn failed(service_result_agg_cid: Rc<CID<ServiceResultAggregate>>) -> CallResult {
|
||||
CallResult::Failed(service_result_agg_cid)
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,11 +116,11 @@ impl std::fmt::Display for ExecutedState {
|
||||
right_size: right_subgraph_size,
|
||||
}) => write!(f, "par({left_subgraph_size}, {right_subgraph_size})"),
|
||||
Call(RequestSentBy(sender)) => write!(f, r"{sender}"),
|
||||
Call(Executed(value)) => {
|
||||
write!(f, "executed({value})")
|
||||
Call(Executed(value_ref)) => {
|
||||
write!(f, "executed({value_ref:?})")
|
||||
}
|
||||
Call(CallServiceFailed(ret_code, err_msg)) => {
|
||||
write!(f, r#"call_service_failed({ret_code}, "{err_msg}")"#)
|
||||
Call(Failed(failed_cid)) => {
|
||||
write!(f, "failed({failed_cid:?})")
|
||||
}
|
||||
Fold(FoldResult { lore }) => {
|
||||
writeln!(f, "fold(",)?;
|
||||
@ -145,6 +154,7 @@ impl std::fmt::Display for ValueRef {
|
||||
ValueRef::Stream { cid, generation } => {
|
||||
write!(f, "stream: {cid:?} generation: {generation}")
|
||||
}
|
||||
ValueRef::Unused(cid) => write!(f, "unused: {cid:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,57 +66,3 @@ pub mod par_serializer {
|
||||
deserializer.deserialize_seq(ParVisitor {})
|
||||
}
|
||||
}
|
||||
|
||||
pub mod sender_serializer {
|
||||
use super::*;
|
||||
|
||||
pub fn serialize<S>(value: &Sender, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match value {
|
||||
Sender::PeerId(peer_id) => serializer.serialize_str(peer_id.as_str()),
|
||||
Sender::PeerIdWithCallId { peer_id, call_id } => {
|
||||
let result = format!("{peer_id}: {call_id}");
|
||||
serializer.serialize_str(&result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Sender, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct SenderVisitor;
|
||||
impl<'de> Visitor<'de> for SenderVisitor {
|
||||
type Value = Sender;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("call sender")
|
||||
}
|
||||
|
||||
fn visit_str<E: serde::de::Error>(self, raw_sender: &str) -> Result<Self::Value, E> {
|
||||
let sender = match raw_sender.find(": ") {
|
||||
None => Sender::PeerId(Rc::new(raw_sender.to_string())),
|
||||
Some(pos) => {
|
||||
let peer_id = raw_sender[..pos].to_string();
|
||||
let call_id = &raw_sender[pos + 2..];
|
||||
let call_id = call_id.parse::<u32>().map_err(|e| {
|
||||
serde::de::Error::custom(format!(
|
||||
"failed to parse call_id of a sender {call_id}: {e}"
|
||||
))
|
||||
})?;
|
||||
Sender::PeerIdWithCallId {
|
||||
peer_id: Rc::new(peer_id),
|
||||
call_id,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(sender)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(SenderVisitor {})
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ use crate::cid_store::CidStore;
|
||||
use crate::CanonCidAggregate;
|
||||
use crate::ExecutionTrace;
|
||||
use crate::JValue;
|
||||
use crate::ServiceResultAggregate;
|
||||
|
||||
use air_utils::measure;
|
||||
use polyplets::SecurityTetraplet;
|
||||
@ -130,16 +131,19 @@ impl Versions {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct CidInfo {
|
||||
/// Map CID to value
|
||||
/// Map CID to value.
|
||||
pub value_store: CidStore<JValue>,
|
||||
|
||||
/// Map CID to a tetraplet
|
||||
/// Map CID to a tetraplet.
|
||||
pub tetraplet_store: CidStore<SecurityTetraplet>,
|
||||
|
||||
/// Map CID to a canon value
|
||||
/// Map CID to a canon value.
|
||||
pub canon_store: CidStore<CanonCidAggregate>,
|
||||
|
||||
/// Map CID to a service result aggregate.
|
||||
pub service_result_store: CidStore<ServiceResultAggregate>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
Reference in New Issue
Block a user