runmat_execution/protocol/
mod.rs1use std::collections::BTreeSet;
2
3use minicbor::{Decoder, Encoder};
4use serde::{Deserialize, Serialize};
5
6use crate::schema::{PROTOCOL_MAJOR_V1, PROTOCOL_MINOR_V1};
7use crate::ContractError;
8
9#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
10pub struct ProtocolVersion {
11 pub major: u16,
12 pub minor: u16,
13}
14
15impl ProtocolVersion {
16 pub const V1: Self = Self {
17 major: PROTOCOL_MAJOR_V1,
18 minor: PROTOCOL_MINOR_V1,
19 };
20}
21
22#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
23pub struct ProtocolLimits {
24 pub max_message_bytes: u32,
25 pub max_payload_bytes: u32,
26 pub max_collection_items: u32,
27 pub max_nesting_depth: u16,
28}
29
30impl Default for ProtocolLimits {
31 fn default() -> Self {
32 Self {
33 max_message_bytes: 16 * 1024 * 1024,
34 max_payload_bytes: 16 * 1024 * 1024 - 128,
35 max_collection_items: 1_000_000,
36 max_nesting_depth: 64,
37 }
38 }
39}
40
41#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
42pub struct ProtocolHello {
43 pub supported_majors: Vec<u16>,
44 pub maximum_minor_by_major: Vec<(u16, u16)>,
45 pub implementation: String,
46 pub capabilities: BTreeSet<String>,
47 pub limits: ProtocolLimits,
48}
49
50impl ProtocolHello {
51 pub fn v1(
52 implementation: impl Into<String>,
53 capabilities: impl IntoIterator<Item = String>,
54 ) -> Self {
55 Self {
56 supported_majors: vec![PROTOCOL_MAJOR_V1],
57 maximum_minor_by_major: vec![(PROTOCOL_MAJOR_V1, PROTOCOL_MINOR_V1)],
58 implementation: implementation.into(),
59 capabilities: capabilities.into_iter().collect(),
60 limits: ProtocolLimits::default(),
61 }
62 }
63}
64
65pub fn negotiate(
66 left: &ProtocolHello,
67 right: &ProtocolHello,
68) -> Result<ProtocolVersion, ContractError> {
69 let major = left
70 .supported_majors
71 .iter()
72 .filter(|major| right.supported_majors.contains(major))
73 .max()
74 .copied()
75 .ok_or_else(|| ContractError::invalid("protocol", "no shared protocol major"))?;
76 let left_minor = minor_for(left, major)?;
77 let right_minor = minor_for(right, major)?;
78 Ok(ProtocolVersion {
79 major,
80 minor: left_minor.min(right_minor),
81 })
82}
83
84fn minor_for(hello: &ProtocolHello, major: u16) -> Result<u16, ContractError> {
85 hello
86 .maximum_minor_by_major
87 .iter()
88 .find_map(|(candidate, minor)| (*candidate == major).then_some(*minor))
89 .ok_or_else(|| {
90 ContractError::invalid(
91 "protocol hello",
92 format!("major {major} lacks a maximum minor"),
93 )
94 })
95}
96
97#[derive(Clone, Debug, Eq, PartialEq)]
98pub struct Envelope {
99 pub version: ProtocolVersion,
100 pub message_kind: u16,
101 pub flags: u32,
102 pub sequence: u64,
103 pub payload: Vec<u8>,
104}
105
106impl Envelope {
107 pub fn encode(&self, limits: ProtocolLimits) -> Result<Vec<u8>, ContractError> {
108 if self.payload.len() > limits.max_payload_bytes as usize {
109 return Err(ContractError::Limit {
110 field: "protocol payload bytes",
111 limit: limits.max_payload_bytes.into(),
112 });
113 }
114 let mut output = Vec::with_capacity(self.payload.len() + 32);
115 let mut encoder = Encoder::new(&mut output);
116 encoder
117 .map(6)
118 .and_then(|encoder| encoder.u8(0))
119 .and_then(|encoder| encoder.u16(self.version.major))
120 .and_then(|encoder| encoder.u8(1))
121 .and_then(|encoder| encoder.u16(self.version.minor))
122 .and_then(|encoder| encoder.u8(2))
123 .and_then(|encoder| encoder.u16(self.message_kind))
124 .and_then(|encoder| encoder.u8(3))
125 .and_then(|encoder| encoder.u32(self.flags))
126 .and_then(|encoder| encoder.u8(4))
127 .and_then(|encoder| encoder.u64(self.sequence))
128 .and_then(|encoder| encoder.u8(5))
129 .and_then(|encoder| encoder.bytes(&self.payload))
130 .map_err(protocol_encode_error)?;
131 if output.len() > limits.max_message_bytes as usize {
132 return Err(ContractError::Limit {
133 field: "protocol message bytes",
134 limit: limits.max_message_bytes.into(),
135 });
136 }
137 Ok(output)
138 }
139
140 pub fn decode(bytes: &[u8], limits: ProtocolLimits) -> Result<Self, ContractError> {
141 if bytes.len() > limits.max_message_bytes as usize {
142 return Err(ContractError::Limit {
143 field: "protocol message bytes",
144 limit: limits.max_message_bytes.into(),
145 });
146 }
147 let mut decoder = Decoder::new(bytes);
148 let fields = decoder
149 .map()
150 .map_err(protocol_decode_error)?
151 .ok_or_else(|| {
152 ContractError::MalformedProtocol("indefinite maps are prohibited".into())
153 })?;
154 if fields > 64 {
155 return Err(ContractError::Limit {
156 field: "protocol envelope fields",
157 limit: 64,
158 });
159 }
160
161 let mut major = None;
162 let mut minor = None;
163 let mut message_kind = None;
164 let mut flags = None;
165 let mut sequence = None;
166 let mut payload = None;
167 let mut previous_key = None;
168
169 for _ in 0..fields {
170 let key = decoder.u16().map_err(protocol_decode_error)?;
171 if previous_key.is_some_and(|previous| previous >= key) {
172 return Err(ContractError::MalformedProtocol(
173 "envelope keys must be unique and ascending".into(),
174 ));
175 }
176 previous_key = Some(key);
177 match key {
178 0 => major = Some(decoder.u16().map_err(protocol_decode_error)?),
179 1 => minor = Some(decoder.u16().map_err(protocol_decode_error)?),
180 2 => message_kind = Some(decoder.u16().map_err(protocol_decode_error)?),
181 3 => flags = Some(decoder.u32().map_err(protocol_decode_error)?),
182 4 => sequence = Some(decoder.u64().map_err(protocol_decode_error)?),
183 5 => {
184 let encoded = decoder.bytes().map_err(protocol_decode_error)?;
185 if encoded.len() > limits.max_payload_bytes as usize {
186 return Err(ContractError::Limit {
187 field: "protocol payload bytes",
188 limit: limits.max_payload_bytes.into(),
189 });
190 }
191 payload = Some(encoded.to_vec());
192 }
193 _ => decoder.skip().map_err(protocol_decode_error)?,
194 }
195 }
196 if decoder.position() != bytes.len() {
197 return Err(ContractError::MalformedProtocol(
198 "trailing bytes after envelope".into(),
199 ));
200 }
201 Ok(Self {
202 version: ProtocolVersion {
203 major: required("major", major)?,
204 minor: required("minor", minor)?,
205 },
206 message_kind: required("message kind", message_kind)?,
207 flags: required("flags", flags)?,
208 sequence: required("sequence", sequence)?,
209 payload: required("payload", payload)?,
210 })
211 }
212}
213
214fn required<T>(field: &'static str, value: Option<T>) -> Result<T, ContractError> {
215 value.ok_or_else(|| ContractError::MalformedProtocol(format!("missing {field}")))
216}
217
218fn protocol_encode_error<E: std::fmt::Display>(error: E) -> ContractError {
219 ContractError::MalformedProtocol(error.to_string())
220}
221
222fn protocol_decode_error(error: minicbor::decode::Error) -> ContractError {
223 ContractError::MalformedProtocol(error.to_string())
224}