1use serde::{Deserialize, Serialize};
2
3use crate::identity::{Digest, NodeLeaseId, ValueId, WorkerId};
4use crate::ContractError;
5
6mod identity;
7mod validation;
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
10pub struct ValueLimits {
11 pub max_depth: u16,
12 pub max_nodes: u64,
13 pub max_inline_bytes: u64,
14 pub max_elements: u64,
15 pub max_fields: u32,
16 pub max_text_bytes: u64,
17}
18
19impl Default for ValueLimits {
20 fn default() -> Self {
21 Self {
22 max_depth: 64,
23 max_nodes: 1_000_000,
24 max_inline_bytes: 1024 * 1024,
25 max_elements: 100_000_000,
26 max_fields: 100_000,
27 max_text_bytes: 16 * 1024 * 1024,
28 }
29 }
30}
31
32#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
33#[serde(rename_all = "snake_case", tag = "form", content = "value")]
34pub enum ValuePayload {
35 Inline(Box<InlineValue>),
36 Object(Box<ValueRef>),
37}
38
39impl ValuePayload {
40 pub fn validate(&self, limits: ValueLimits) -> Result<(), ContractError> {
41 validation::validate(self, limits)
42 }
43
44 pub fn logical_digest(&self) -> Result<Digest, ContractError> {
45 identity::logical_digest(self)
46 }
47}
48
49#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
50#[serde(rename_all = "snake_case", tag = "type", content = "value")]
51pub enum InlineValue {
52 Null,
53 Logical(bool),
54 F64Bits(u64),
55 I8(i8),
56 I16(i16),
57 I32(i32),
58 I64(i64),
59 U8(u8),
60 U16(u16),
61 U32(u32),
62 U64(u64),
63 ComplexF64Bits {
64 real: u64,
65 imaginary: u64,
66 },
67 String(String),
68 Char {
69 shape: Vec<u64>,
70 code_points: Vec<u32>,
71 },
72 StringArray {
73 shape: Vec<u64>,
74 values: Vec<String>,
75 },
76 Dense(DenseValue),
77 Sparse(SparseValue),
78 Symbolic(RegisteredData),
79 Cell {
80 shape: Vec<u64>,
81 values: Vec<ValuePayload>,
82 },
83 Struct(Vec<StructField>),
84 OutputList(Vec<ValuePayload>),
85 Exception(ExceptionValue),
86 Callable(CallableValue),
87 ImmutableValueClass(RegisteredData),
88}
89
90#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
91pub struct DenseValue {
92 pub element_type: ElementType,
93 pub shape: Vec<u64>,
94 pub little_endian_data: Vec<u8>,
95}
96
97#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
98pub struct SparseValue {
99 pub element_type: ElementType,
100 pub rows: u64,
101 pub columns: u64,
102 pub column_offsets: Vec<u64>,
103 pub row_indices: Vec<u64>,
104 pub little_endian_data: Vec<u8>,
105}
106
107#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
108#[serde(rename_all = "snake_case")]
109#[repr(u8)]
110pub enum ElementType {
111 Logical = 0,
112 F32 = 1,
113 F64 = 2,
114 ComplexF64 = 3,
115 I8 = 4,
116 I16 = 5,
117 I32 = 6,
118 I64 = 7,
119 U8 = 8,
120 U16 = 9,
121 U32 = 10,
122 U64 = 11,
123 ComplexF32 = 12,
124 ComplexI8 = 13,
125 ComplexI16 = 14,
126 ComplexI32 = 15,
127 ComplexI64 = 16,
128 ComplexU8 = 17,
129 ComplexU16 = 18,
130 ComplexU32 = 19,
131 ComplexU64 = 20,
132}
133
134impl ElementType {
135 const fn byte_width(self) -> u64 {
136 match self {
137 Self::Logical | Self::I8 | Self::U8 => 1,
138 Self::I16 | Self::U16 => 2,
139 Self::F32 | Self::I32 | Self::U32 => 4,
140 Self::F64 | Self::I64 | Self::U64 => 8,
141 Self::ComplexI8 | Self::ComplexU8 => 2,
142 Self::ComplexF32 | Self::ComplexI32 | Self::ComplexU32 => 8,
143 Self::ComplexI16 | Self::ComplexU16 => 4,
144 Self::ComplexI64 | Self::ComplexU64 => 16,
145 Self::ComplexF64 => 16,
146 }
147 }
148}
149
150#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
151pub struct StructField {
152 pub name: String,
153 pub value: ValuePayload,
154}
155
156#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
157pub struct RegisteredData {
158 pub type_identity: String,
159 pub schema_version: u32,
160 pub fields: Vec<RegisteredField>,
161}
162
163#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
164pub struct RegisteredField {
165 pub name: String,
166 pub value: ValuePayload,
167}
168
169#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
170pub struct ExceptionValue {
171 pub identifier: String,
172 pub message: String,
173 pub stack: Vec<String>,
174 pub causes: Vec<ExceptionValue>,
175}
176
177#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
178pub struct CallableValue {
179 pub owner_identity: String,
180 pub qualified_name: String,
181 pub callable_digest: Digest,
182 pub captures: Vec<ValuePayload>,
183}
184
185impl CallableValue {
186 pub fn identity_digest(owner_identity: &str, qualified_name: &str) -> Digest {
187 let mut identity = b"runmat-callable-v1\0".to_vec();
188 identity.extend_from_slice(owner_identity.as_bytes());
189 identity.push(0);
190 identity.extend_from_slice(qualified_name.as_bytes());
191 Digest::sha256(identity)
192 }
193
194 pub fn validate_identity(&self) -> Result<(), ContractError> {
195 if self.callable_digest != Self::identity_digest(&self.owner_identity, &self.qualified_name)
196 {
197 return Err(ContractError::invalid(
198 "callable value",
199 "callable digest does not match its owner and qualified name",
200 ));
201 }
202 Ok(())
203 }
204}
205
206#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
207pub struct ValueRef {
208 pub schema_version: u16,
209 pub id: ValueId,
210 pub logical_digest: Digest,
211 pub encoded_length: u64,
212 pub media_type: String,
213 pub value_schema: String,
214 pub encryption_context: Digest,
215 pub kind: ValueRefKind,
216 pub authorization_scope: String,
217 pub resident_fence: Option<ResidentFence>,
218}
219
220#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
221#[serde(rename_all = "snake_case")]
222pub enum ValueRefKind {
223 DriverObject,
224 WorkerObject,
225 ProjectObject,
226 BroadcastObject,
227 SlicedObject,
228 ResultObject,
229 CheckpointObject,
230 ResidentObject,
231}
232
233#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
234pub struct ResidentFence {
235 pub worker_id: WorkerId,
236 pub node_lease_id: NodeLeaseId,
237 pub process_generation: u64,
238 pub device_identity: Option<String>,
239}