aranya_runtime/vm_policy/
protocol.rs1extern crate alloc;
2
3use alloc::{borrow::Cow, collections::BTreeMap, string::String, sync::Arc};
4
5use aranya_crypto::UserId;
6use aranya_policy_vm::{Struct, Value};
7use serde::{Deserialize, Serialize};
8
9use crate::{
10 command::{Command, CommandId, Priority},
11 Address, Prior,
12};
13
14#[derive(Debug, Serialize, Deserialize)]
16pub enum VmProtocolData<'a> {
17 Init {
18 policy: [u8; 8],
19 author_id: UserId,
20 #[serde(borrow)]
21 kind: &'a str,
22 #[serde(borrow)]
23 serialized_fields: &'a [u8],
24 #[serde(borrow)]
25 signature: &'a [u8],
26 },
27 Merge {
28 left: Address,
29 right: Address,
30 },
31 Basic {
32 parent: Address,
33 author_id: UserId,
34 #[serde(borrow)]
35 kind: &'a str,
36 #[serde(borrow)]
37 serialized_fields: &'a [u8],
38 #[serde(borrow)]
39 signature: &'a [u8],
40 },
41}
42
43#[derive(Debug)]
47pub struct VmProtocol<'a> {
48 data: &'a [u8],
50 id: CommandId,
52 unpacked: VmProtocolData<'a>,
54 priority_map: Arc<BTreeMap<String, u32>>,
57}
58
59impl<'a> VmProtocol<'a> {
60 pub fn new(
61 data: &'a [u8],
62 id: CommandId,
63 unpacked: VmProtocolData<'a>,
64 priority_map: Arc<BTreeMap<String, u32>>,
65 ) -> VmProtocol<'a> {
66 VmProtocol {
67 data,
68 id,
69 unpacked,
70 priority_map,
71 }
72 }
73}
74
75impl Command for VmProtocol<'_> {
76 fn priority(&self) -> Priority {
77 match self.unpacked {
78 VmProtocolData::Init { .. } => Priority::Init,
79 VmProtocolData::Merge { .. } => Priority::Merge,
80 VmProtocolData::Basic { kind, .. } => {
81 Priority::Basic(self.priority_map.get(kind).copied().unwrap_or_default())
82 }
83 }
84 }
85
86 fn id(&self) -> CommandId {
87 self.id
88 }
89
90 fn parent(&self) -> Prior<Address> {
91 match self.unpacked {
92 VmProtocolData::Init { .. } => Prior::None,
93 VmProtocolData::Merge { left, right, .. } => Prior::Merge(left, right),
94 VmProtocolData::Basic { parent, .. } => Prior::Single(parent),
95 }
96 }
97
98 fn policy(&self) -> Option<&[u8]> {
99 match self.unpacked {
100 VmProtocolData::Init { ref policy, .. } => Some(policy),
101 _ => None,
102 }
103 }
104
105 fn bytes(&self) -> &[u8] {
106 self.data
107 }
108}
109
110#[derive(Clone, Debug)]
111pub struct Envelope<'a> {
112 pub parent_id: CommandId,
113 pub author_id: UserId,
114 pub command_id: CommandId,
115 pub payload: Cow<'a, [u8]>,
116 pub signature: Cow<'a, [u8]>,
117}
118
119impl From<Envelope<'_>> for Struct {
120 fn from(e: Envelope<'_>) -> Self {
121 Self::new(
122 "Envelope",
123 [
124 ("parent_id".into(), e.parent_id.into_id().into()),
125 ("author_id".into(), e.author_id.into_id().into()),
126 ("command_id".into(), e.command_id.into_id().into()),
127 ("payload".into(), e.payload.into_owned().into()),
128 ("signature".into(), e.signature.into_owned().into()),
129 ],
130 )
131 }
132}
133
134impl TryFrom<Struct> for Envelope<'_> {
135 type Error = EnvelopeError;
136
137 fn try_from(
138 Struct {
139 name,
140 ref mut fields,
141 }: Struct,
142 ) -> Result<Self, Self::Error> {
143 if name != "Envelope" {
144 return Err(EnvelopeError::InvalidName(name));
145 }
146
147 Ok(Self {
148 parent_id: get::<aranya_crypto::Id>(fields, "parent_id")?.into(),
149 author_id: get::<aranya_crypto::Id>(fields, "author_id")?.into(),
150 command_id: get::<aranya_crypto::Id>(fields, "command_id")?.into(),
151 payload: Cow::Owned(get(fields, "payload")?),
152 signature: Cow::Owned(get(fields, "signature")?),
153 })
154 }
155}
156
157fn get<T: TryFrom<Value>>(
158 fields: &mut BTreeMap<String, Value>,
159 key: &'static str,
160) -> Result<T, EnvelopeError> {
161 fields
162 .remove(key)
163 .ok_or(EnvelopeError::MissingField(key))?
164 .try_into()
165 .map_err(|_| EnvelopeError::InvalidType(key))
166}
167
168#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
169pub enum EnvelopeError {
170 #[error("invalid struct name {0:?}")]
171 InvalidName(String),
172 #[error("missing field {0:?}")]
173 MissingField(&'static str),
174 #[error("invalid type for field {0:?}")]
175 InvalidType(&'static str),
176}