aranya_runtime/vm_policy/
protocol.rs

1extern crate alloc;
2
3use alloc::{borrow::Cow, collections::BTreeMap, string::String, sync::Arc};
4use core::fmt;
5
6use aranya_crypto::UserId;
7use aranya_policy_vm::{Struct, Value};
8use serde::{Deserialize, Serialize};
9
10use crate::{
11    command::{Command, CommandId, Priority},
12    Address, Prior,
13};
14
15/// The data inside a [VmProtocol]. It gets serialized and deserialized over the wire.
16#[derive(Debug, Serialize, Deserialize)]
17pub enum VmProtocolData<'a> {
18    Init {
19        policy: [u8; 8],
20        author_id: UserId,
21        #[serde(borrow)]
22        kind: &'a str,
23        #[serde(borrow)]
24        serialized_fields: &'a [u8],
25        #[serde(borrow)]
26        signature: &'a [u8],
27    },
28    Merge {
29        left: Address,
30        right: Address,
31    },
32    Basic {
33        parent: Address,
34        author_id: UserId,
35        #[serde(borrow)]
36        kind: &'a str,
37        #[serde(borrow)]
38        serialized_fields: &'a [u8],
39        #[serde(borrow)]
40        signature: &'a [u8],
41    },
42}
43
44/// The Command implementation as used by the VM. It deserializes the interior data into a
45/// [VmProtocolData] struct, and it keeps the original serialized copy around for quick
46/// access to that.
47#[derive(Debug)]
48pub struct VmProtocol<'a> {
49    /// Reference to the serialized data underlying the command
50    data: &'a [u8],
51    /// The ID of the command
52    id: CommandId,
53    /// The deserialized data
54    unpacked: VmProtocolData<'a>,
55    /// A mapping between command names and priorities, shared with the underlying
56    /// [`super::VmPolicy`] and other [`VmProtocol`] instances.
57    priority_map: Arc<BTreeMap<String, u32>>,
58}
59
60impl<'a> VmProtocol<'a> {
61    pub fn new(
62        data: &'a [u8],
63        id: CommandId,
64        unpacked: VmProtocolData<'a>,
65        priority_map: Arc<BTreeMap<String, u32>>,
66    ) -> VmProtocol<'a> {
67        VmProtocol {
68            data,
69            id,
70            unpacked,
71            priority_map,
72        }
73    }
74}
75
76impl Command for VmProtocol<'_> {
77    fn priority(&self) -> Priority {
78        match self.unpacked {
79            VmProtocolData::Init { .. } => Priority::Init,
80            VmProtocolData::Merge { .. } => Priority::Merge,
81            VmProtocolData::Basic { kind, .. } => {
82                Priority::Basic(self.priority_map.get(kind).copied().unwrap_or_default())
83            }
84        }
85    }
86
87    fn id(&self) -> CommandId {
88        self.id
89    }
90
91    fn parent(&self) -> Prior<Address> {
92        match self.unpacked {
93            VmProtocolData::Init { .. } => Prior::None,
94            VmProtocolData::Merge { left, right, .. } => Prior::Merge(left, right),
95            VmProtocolData::Basic { parent, .. } => Prior::Single(parent),
96        }
97    }
98
99    fn policy(&self) -> Option<&[u8]> {
100        match self.unpacked {
101            VmProtocolData::Init { ref policy, .. } => Some(policy),
102            _ => None,
103        }
104    }
105
106    fn bytes(&self) -> &[u8] {
107        self.data
108    }
109}
110
111#[derive(Clone, Debug)]
112pub struct Envelope<'a> {
113    pub parent_id: CommandId,
114    pub author_id: UserId,
115    pub command_id: CommandId,
116    pub payload: Cow<'a, [u8]>,
117    pub signature: Cow<'a, [u8]>,
118}
119
120impl From<Envelope<'_>> for Struct {
121    fn from(e: Envelope<'_>) -> Self {
122        Self::new(
123            "Envelope",
124            [
125                ("parent_id".into(), e.parent_id.into_id().into()),
126                ("author_id".into(), e.author_id.into_id().into()),
127                ("command_id".into(), e.command_id.into_id().into()),
128                ("payload".into(), e.payload.into_owned().into()),
129                ("signature".into(), e.signature.into_owned().into()),
130            ],
131        )
132    }
133}
134
135impl TryFrom<Struct> for Envelope<'_> {
136    type Error = EnvelopeError;
137
138    fn try_from(
139        Struct {
140            name,
141            ref mut fields,
142        }: Struct,
143    ) -> Result<Self, Self::Error> {
144        if name != "Envelope" {
145            return Err(EnvelopeError::InvalidName(name));
146        }
147
148        Ok(Self {
149            parent_id: get::<aranya_crypto::Id>(fields, "parent_id")?.into(),
150            author_id: get::<aranya_crypto::Id>(fields, "author_id")?.into(),
151            command_id: get::<aranya_crypto::Id>(fields, "command_id")?.into(),
152            payload: Cow::Owned(get(fields, "payload")?),
153            signature: Cow::Owned(get(fields, "signature")?),
154        })
155    }
156}
157
158fn get<T: TryFrom<Value>>(
159    fields: &mut BTreeMap<String, Value>,
160    key: &'static str,
161) -> Result<T, EnvelopeError> {
162    fields
163        .remove(key)
164        .ok_or(EnvelopeError::MissingField(key))?
165        .try_into()
166        .map_err(|_| EnvelopeError::InvalidType(key))
167}
168
169#[derive(Clone, Debug, PartialEq, Eq)]
170pub enum EnvelopeError {
171    InvalidName(String),
172    MissingField(&'static str),
173    InvalidType(&'static str),
174}
175
176impl fmt::Display for EnvelopeError {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        match self {
179            Self::InvalidName(name) => write!(f, "invalid struct name {name:?}"),
180            Self::MissingField(field) => write!(f, "missing field {field:?}"),
181            Self::InvalidType(field) => write!(f, "invalid type for field {field:?}"),
182        }
183    }
184}
185
186impl core::error::Error for EnvelopeError {}