1use crate::result_mode::{ResultMode, ResultModeVerify};
15use base_crypto::fab::AlignedValue;
16use base_crypto::repr::MemWrite;
17use derive_where::derive_where;
18#[cfg(feature = "proptest")]
19use proptest::prelude::*;
20#[cfg(feature = "proptest")]
21use proptest_derive::Arbitrary;
22use runtime_state::state::StateValue;
23use serde::{Deserialize, Serialize};
24#[cfg(feature = "proptest")]
25use serialize::randomised_serialization_test;
26use serialize::{Deserializable, Serializable, Tagged, tag_enforcement_test};
27use std::fmt::Debug;
28use storage::db::DB;
29#[cfg(feature = "proptest")]
30use storage::db::InMemoryDB;
31use storage::storable::Loader;
32use storage::storage::Array;
33use storage::{DefaultDB, Storable, arena::ArenaKey};
34use transient_crypto::curve::Fr;
35use transient_crypto::repr::FieldRepr;
36
37#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Serializable, Storable)]
38#[storable(base)]
39#[cfg_attr(feature = "proptest", derive(Arbitrary))]
40#[serde(tag = "tag", content = "value", rename_all = "camelCase")]
41#[tag = "impact-idx-key"]
42pub enum Key {
43 Value(AlignedValue),
44 Stack,
45}
46tag_enforcement_test!(Key);
47
48impl Debug for Key {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 match self {
51 Key::Value(v) => v.fmt(f),
52 Key::Stack => write!(f, "STK"),
53 }
54 }
55}
56
57impl TryFrom<Key> for AlignedValue {
58 type Error = ();
59 fn try_from(value: Key) -> Result<Self, Self::Error> {
60 match value {
61 Key::Value(v) => Ok(v),
62 Key::Stack => Err(()),
63 }
64 }
65}
66
67impl FieldRepr for Key {
68 fn field_repr<W: MemWrite<Fr>>(&self, writer: &mut W) {
69 match self {
70 Key::Stack => writer.write(&[(-1).into()]),
71 Key::Value(v) => v.field_repr(writer),
72 }
73 }
74
75 fn field_size(&self) -> usize {
76 match self {
77 Key::Stack => 1,
78 Key::Value(v) => v.field_size(),
79 }
80 }
81}
82
83#[non_exhaustive]
84#[derive_where(Clone, Eq, PartialEq; M)]
85#[derive(Serialize, Deserialize, Storable)]
86#[serde(bound(
87 serialize = "M::ReadResult : Serialize",
88 deserialize = "M::ReadResult : Deserialize<'de>"
89))]
90#[serde(rename_all = "lowercase", expecting = "operation")]
91#[cfg_attr(feature = "proptest", derive(Arbitrary))]
92#[storable(db = D)]
93#[tag = "impact-op[v1]"]
94#[phantom(M)]
95pub enum Op<M: ResultMode<D>, D: DB = DefaultDB> {
96 Noop {
97 #[cfg_attr(
98 feature = "proptest",
99 proptest(strategy = "any::<u32>().prop_map(|x| x % 0x1FFFFF)")
100 )]
101 n: u32,
102 },
103 Lt,
104 Eq,
105 Type,
106 Size,
107 New,
108 And,
109 Or,
110 Neg,
111 Log,
112 Root,
113 Pop,
114 Popeq {
115 cached: bool,
116 result: M::ReadResult,
117 },
118 Addi {
119 #[cfg_attr(
120 feature = "proptest",
121 proptest(strategy = "any::<u32>().prop_map(|x| x % 0x1FFFFF)")
122 )]
123 immediate: u32,
124 },
125 Subi {
126 #[cfg_attr(
127 feature = "proptest",
128 proptest(strategy = "any::<u32>().prop_map(|x| x % 0x1FFFFF)")
129 )]
130 immediate: u32,
131 },
132 Push {
133 storage: bool,
134 value: StateValue<D>,
135 },
136 Branch {
137 #[cfg_attr(
138 feature = "proptest",
139 proptest(strategy = "any::<u32>().prop_map(|x| x % 0x1FFFFF)")
140 )]
141 skip: u32,
142 },
143 Jmp {
144 #[cfg_attr(
145 feature = "proptest",
146 proptest(strategy = "any::<u32>().prop_map(|x| x % 0x1FFFFF)")
147 )]
148 skip: u32,
149 },
150 Add,
151 Sub,
152 Concat {
153 cached: bool,
154 #[cfg_attr(
155 feature = "proptest",
156 proptest(strategy = "any::<u32>().prop_map(|x| x % 0x1FFFFF)")
157 )]
158 n: u32,
159 },
160 Member,
161 Rem {
162 cached: bool,
163 },
164 Dup {
165 #[cfg_attr(
166 feature = "proptest",
167 proptest(strategy = "any::<u8>().prop_map(|x| x % 16)")
168 )]
169 n: u8,
170 },
171 Swap {
172 #[cfg_attr(
173 feature = "proptest",
174 proptest(strategy = "any::<u8>().prop_map(|x| x % 16)")
175 )]
176 n: u8,
177 },
178 Idx {
179 cached: bool,
180 #[serde(rename = "pushPath")]
181 push_path: bool,
182 #[cfg_attr(
183 feature = "proptest",
184 proptest(
185 strategy = "proptest::collection::vec(Key::arbitrary(), 1..2).prop_map_into()"
186 )
187 )]
188 path: Array<Key, D>,
189 },
190 Ins {
191 cached: bool,
192 #[cfg_attr(
193 feature = "proptest",
194 proptest(strategy = "any::<u8>().prop_map(|x| x % 16)", filter = "|v| *v != 0")
195 )]
196 n: u8,
197 },
198 Ckpt,
199}
200tag_enforcement_test!(Op<ResultModeVerify>);
201
202#[macro_export]
203macro_rules! key {
204 (stack) => {
205 Key::Stack
206 };
207 ($val:expr_2021) => {
208 Key::Value($val.into())
209 };
210}
211
212#[macro_export]
213macro_rules! op {
214 (noop $val:expr_2021) => { Op::Noop { n: $val } };
215 (lt) => { Op::Lt };
216 (eq) => { Op::Eq };
217 (type) => { Op::Type };
218 (size) => { Op::Size };
219 (new) => { Op::New };
220 (and) => { Op::And };
221 (or) => { Op::Or };
222 (neg) => { Op::Neg };
223 (log) => { Op::Log };
224 (root) => { Op::Root };
225 (pop) => { Op::Pop };
226 (popeq $res:expr_2021) => { Op::Popeq { cached: false, result: $res } };
227 (popeqc $res:expr_2021) => { Op::Popeq { cached: true, result: $res } };
228 (addi $imm:expr_2021) => { Op::Addi { immediate: $imm } };
229 (subi $imm:expr_2021) => { Op::Subi { immediate: $imm } };
230 (push $val:tt) => { Op::Push { storage: false, value: stval! $val } };
231 (pushs $val:tt) => { Op::Push { storage: true, value: stval! $val } };
232 (branch $skip:expr_2021) => { Op::Branch { skip: $skip } };
233 (jmp $skip:expr_2021) => { Op::Jmp { skip: $skip } };
234 (add) => { Op::Add };
235 (sub) => { Op::Sub };
236 (concat $n:expr_2021) => { Op::Concat { cached: false, n: $n } };
237 (concatc $n:expr_2021) => { Op::Concat { cached: true, n: $n } };
238 (member) => { Op::Member };
239 (rem) => { Op::Rem { cached: false } };
240 (remc) => { Op::Rem { cached: true } };
241 (dup $n:expr_2021) => { Op::Dup { n: $n } };
242 (swap $n:expr_2021) => { Op::Swap { n: $n } };
243 (idx [$($key:tt),*]) => { Op::Idx { cached: false, push_path: false, path: vec![$(key!($key)),*].into_iter().collect() }};
244 (idxc [$($key:tt),*]) => { Op::Idx { cached: true, push_path: false, path: vec![$(key!($key)),*].into_iter().collect() }};
245 (idxp [$($key:tt),*]) => { Op::Idx { cached: false, push_path: true, path: vec![$(key!($key)),*].into_iter().collect() }};
246 (idxpc [$($key:tt),*]) => { Op::Idx { cached: true, push_path: true, path: vec![$(key!($key)),*].into_iter().collect() }};
247 (ins $n:expr_2021) => { Op::Ins { cached: false, n: $n } };
248 (insc $n:expr_2021) => { Op::Ins { cached: true, n: $n } };
249 (ckpt) => { Op::Ckpt };
250}
251
252#[macro_export]
253macro_rules! ops_int {
254 [] => { std::iter::empty() };
255 [;] => { std::iter::empty() };
256 [$op0:tt ; $($ops:tt)*] => { std::iter::once(op!($op0)).chain(ops_int!($($ops)*)) };
257 [$op0:tt $op1:tt ; $($ops:tt)*] => { std::iter::once(op!($op0 $op1)).chain(ops_int!($($ops)*)) };
258 [$op0:tt $op1:tt $op2:tt ; $($ops:tt)*] => { std::iter::once(op!($op0 $op1 $op2)).chain(ops_int!($($ops)*)) };
259 [$op0:tt $op1:tt $op2:tt $op3:tt ; $($ops:tt)*] => { std::iter::once(op!($op0 $op1 $op2 $op3)).chain(ops_int!($($ops)*)) };
260 [$($ops:tt)*] => { std::iter::once(op!($($ops)*)) };
261}
262
263#[macro_export]
264macro_rules! ops {
265 [$($tts:tt)*] => { ops_int!($($tts)*).collect::<Vec<_>>() };
266}
267
268impl<M: ResultMode<D>, D: DB> Debug for Op<M, D> {
269 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270 use Op::*;
271 match self {
272 Noop { n } => write!(f, "noop {n}"),
273 Lt => write!(f, "lt"),
274 Eq => write!(f, "eq"),
275 Type => write!(f, "type"),
276 Size => write!(f, "size"),
277 New => write!(f, "new"),
278 And => write!(f, "and"),
279 Or => write!(f, "or"),
280 Neg => write!(f, "neg"),
281 Log => write!(f, "log"),
282 Root => write!(f, "root"),
283 Pop => write!(f, "pop"),
284 Popeq {
285 cached: false,
286 result,
287 } => write!(f, "popeq {result:?}"),
288 Popeq {
289 cached: true,
290 result,
291 } => write!(f, "popeqc {result:?}"),
292 Addi { immediate } => write!(f, "addi {immediate:?}"),
293 Subi { immediate } => write!(f, "subi {immediate:?}"),
294 Push {
295 storage: false,
296 value,
297 } => write!(f, "push {value:?}"),
298 Push {
299 storage: true,
300 value,
301 } => write!(f, "pushs {value:?}"),
302 Branch { skip } => write!(f, "branch {skip}"),
303 Jmp { skip } => write!(f, "jmp {skip}"),
304 Add => write!(f, "add"),
305 Sub => write!(f, "sub"),
306 Concat { cached: false, n } => write!(f, "concat {n}"),
307 Concat { cached: true, n } => write!(f, "concatc {n}"),
308 Member => write!(f, "member"),
309 Rem { cached: false } => write!(f, "rem"),
310 Rem { cached: true } => write!(f, "remc"),
311 Dup { n } => write!(f, "dup {n}"),
312 Swap { n } => write!(f, "swap {n}"),
313 Idx {
314 cached,
315 push_path,
316 path,
317 } => {
318 write!(f, "idx")?;
319 if *push_path {
320 write!(f, "p")?;
321 }
322 if *cached {
323 write!(f, "c")?;
324 }
325 write!(f, " [")?;
326 let mut is_first = true;
327 for key in path.iter() {
328 if is_first {
329 is_first = false;
330 } else {
331 write!(f, ", ")?;
332 }
333 write!(f, "{key:?}")?;
334 }
335 write!(f, "]")
336 }
337 Ins { cached: false, n } => write!(f, "ins {n}"),
338 Ins { cached: true, n } => write!(f, "insc {n}"),
339 Ckpt => write!(f, "ckpt"),
340 }
341 }
342}
343
344impl<M: ResultMode<D>, D: DB> Op<M, D> {
345 pub fn translate<M2: ResultMode<D>, F: FnOnce(M::ReadResult) -> M2::ReadResult>(
346 self,
347 f: F,
348 ) -> Op<M2, D> {
349 match self {
350 Op::Noop { n } => Op::Noop { n },
351 Op::Lt => Op::Lt,
352 Op::Eq => Op::Eq,
353 Op::Type => Op::Type,
354 Op::Size => Op::Size,
355 Op::New => Op::New,
356 Op::And => Op::And,
357 Op::Or => Op::Or,
358 Op::Neg => Op::Neg,
359 Op::Log => Op::Log,
360 Op::Root => Op::Root,
361 Op::Pop => Op::Pop,
362 Op::Popeq { cached, result } => Op::Popeq {
363 cached,
364 result: f(result),
365 },
366 Op::Addi { immediate } => Op::Addi { immediate },
367 Op::Subi { immediate } => Op::Subi { immediate },
368 Op::Push { storage, value } => Op::Push { storage, value },
369 Op::Branch { skip } => Op::Branch { skip },
370 Op::Jmp { skip } => Op::Jmp { skip },
371 Op::Add => Op::Add,
372 Op::Sub => Op::Sub,
373 Op::Concat { cached, n } => Op::Concat { cached, n },
374 Op::Member => Op::Member,
375 Op::Rem { cached } => Op::Rem { cached },
376 Op::Dup { n } => Op::Dup { n },
377 Op::Swap { n } => Op::Swap { n },
378 Op::Idx {
379 cached,
380 push_path,
381 path,
382 } => Op::Idx {
383 cached,
384 push_path,
385 path,
386 },
387 Op::Ins { cached, n } => Op::Ins { cached, n },
388 Op::Ckpt => Op::Ckpt,
389 }
390 }
391}
392
393#[cfg(feature = "proptest")]
394#[allow(dead_code)]
395type SimpleOp = Op<ResultModeVerify, InMemoryDB>;
396#[cfg(feature = "proptest")]
397randomised_serialization_test!(SimpleOp);
398
399impl<D: DB> FieldRepr for Op<ResultModeVerify, D> {
400 fn field_repr<W: MemWrite<Fr>>(&self, writer: &mut W) {
401 use Op::*;
402 match self {
403 Noop { n } => writer.write(&vec![0x00.into(); *n as usize]),
404 Lt => writer.write(&[0x01.into()]),
405 Eq => writer.write(&[0x02.into()]),
406 Type => writer.write(&[0x03.into()]),
407 Size => writer.write(&[0x04.into()]),
408 New => writer.write(&[0x05.into()]),
409 And => writer.write(&[0x06.into()]),
410 Or => writer.write(&[0x07.into()]),
411 Neg => writer.write(&[0x08.into()]),
412 Log => writer.write(&[0x09.into()]),
413 Root => writer.write(&[0x0a.into()]),
414 Pop => writer.write(&[0x0b.into()]),
415 Popeq { cached, result } => {
416 writer.write(&[(0x0c + *cached as u8).into()]);
417 result.field_repr(writer);
418 }
419 Addi { immediate } => {
420 writer.write(&[0x0e.into()]);
421 immediate.field_repr(writer);
422 }
423 Subi { immediate } => {
424 writer.write(&[0x0f.into()]);
425 immediate.field_repr(writer);
426 }
427 Push { storage, value } => {
428 writer.write(&[(0x10 + *storage as u8).into()]);
429 value.field_repr(writer);
430 }
431 Branch { skip } => writer.write(&[0x12.into(), (*skip).into()]),
432 Jmp { skip } => writer.write(&[0x13.into(), (*skip).into()]),
433 Add => writer.write(&[0x14.into()]),
434 Sub => writer.write(&[0x15.into()]),
435 Concat { cached: false, n } => writer.write(&[0x16.into(), (*n).into()]),
436 Concat { cached: true, n } => writer.write(&[0x17.into(), (*n).into()]),
437 Member => writer.write(&[0x18.into()]),
438 Rem { cached: false } => writer.write(&[0x19.into()]),
439 Rem { cached: true } => writer.write(&[0x1a.into()]),
440 Dup { n } => writer.write(&[(0x30 | *n).into()]),
441 Swap { n } => writer.write(&[(0x40 | *n).into()]),
442 Idx {
443 cached,
444 push_path,
445 path,
446 } => {
447 if !path.is_empty() {
448 let opcode = match (*cached, *push_path) {
449 (false, false) => 0x50,
450 (true, false) => 0x60,
451 (false, true) => 0x70,
452 (true, true) => 0x80,
453 } | (path.len() as u8 - 1);
454 writer.write(&[opcode.into()]);
455 for entry in path.iter() {
456 entry.field_repr(writer);
457 }
458 }
459 }
460 Ins { cached: false, n } => writer.write(&[(0x90 | *n).into()]),
461 Ins { cached: true, n } => writer.write(&[(0xa0 | *n).into()]),
462 Ckpt => writer.write(&[0xff.into()]),
463 }
464 }
465
466 fn field_size(&self) -> usize {
467 use Op::*;
468 match self {
469 Lt
470 | Eq
471 | Type
472 | Size
473 | New
474 | And
475 | Or
476 | Neg
477 | Log
478 | Root
479 | Pop
480 | Add
481 | Sub
482 | Member
483 | Rem { .. }
484 | Dup { .. }
485 | Swap { .. }
486 | Ins { .. }
487 | Ckpt => 1,
488 Noop { n } => *n as usize,
489 Branch { .. } | Jmp { .. } | Concat { .. } => 2,
490 Addi { immediate } | Subi { immediate } => 1 + immediate.field_size(),
491 Popeq { result, .. } => 1 + result.field_size(),
492 Push { value, .. } => 1 + value.field_size(),
493 Idx { path, .. } => 1 + path.iter().map(|item| item.field_size()).sum::<usize>(),
494 }
495 }
496}
497
498pub use {key, op, ops, ops_int};
499
500#[cfg(test)]
501mod tests {
502 use storage::DefaultDB;
503
504 use super::Op;
505 use crate::result_mode::ResultModeGather;
506 use runtime_state::state::StateValue;
507 use storage::storage::HashMap;
508
509 #[test]
510 fn diagnostic_test_map_serialization_stability() {
511 let op: Op<ResultModeGather, DefaultDB> = Op::Push {
512 storage: false,
513 value: StateValue::Map(HashMap::new()),
514 };
515 let mut ser = Vec::new();
516 serialize::Serializable::serialize(&op, &mut ser).unwrap();
517 let op2: Op<ResultModeGather, DefaultDB> =
518 serialize::Deserializable::deserialize(&mut &ser[..], 0).unwrap();
519 assert_eq!(op, op2);
520 }
521}