1use std::{
2 any::Any,
3 cell::RefCell,
4 collections::btree_map::{BTreeMap, Entry},
5 fmt,
6 marker::PhantomData,
7 mem,
8};
9
10use oasis_core_runtime::{common::crypto::hash::Hash, consensus::roothash, storage::mkvs};
11
12use crate::{
13 context::Context,
14 crypto::{random::RootRng, signature::PublicKey},
15 event::{Event, EventTag, EventTags},
16 modules::core::Error,
17 storage::{MKVSStore, NestedStore, OverlayStore, Store},
18 types::{address::Address, message::MessageEventHookInvocation, transaction},
19};
20
21#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
23#[repr(u8)]
24pub enum Mode {
25 #[default]
27 Execute,
28 Check,
30 Simulate,
32 PreSchedule,
34}
35
36const MODE_CHECK: &str = "check";
37const MODE_EXECUTE: &str = "execute";
38const MODE_SIMULATE: &str = "simulate";
39const MODE_PRE_SCHEDULE: &str = "pre_schedule";
40
41impl fmt::Display for Mode {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 f.write_str(self.into())
44 }
45}
46
47impl From<&Mode> for &'static str {
48 fn from(m: &Mode) -> Self {
49 match m {
50 Mode::Check => MODE_CHECK,
51 Mode::Execute => MODE_EXECUTE,
52 Mode::Simulate => MODE_SIMULATE,
53 Mode::PreSchedule => MODE_PRE_SCHEDULE,
54 }
55 }
56}
57
58#[derive(Clone, Default, Debug)]
60pub struct Environment {
61 mode: Mode,
62 tx: Option<TransactionWithMeta>,
63 internal: bool,
64}
65
66impl Environment {
67 pub fn mode(&self) -> Mode {
69 self.mode
70 }
71
72 pub fn is_check_only(&self) -> bool {
74 matches!(self.mode, Mode::Check | Mode::PreSchedule)
75 }
76
77 pub fn is_pre_schedule(&self) -> bool {
79 matches!(self.mode, Mode::PreSchedule)
80 }
81
82 pub fn is_simulation(&self) -> bool {
84 matches!(self.mode, Mode::Simulate)
85 }
86
87 pub fn is_execute(&self) -> bool {
89 matches!(self.mode, Mode::Execute)
90 }
91
92 pub fn is_transaction(&self) -> bool {
94 self.tx.is_some()
95 }
96
97 pub fn tx_index(&self) -> usize {
103 self.tx
104 .as_ref()
105 .map(|tx| tx.index)
106 .expect("only in transaction environment")
107 }
108
109 pub fn tx_size(&self) -> u32 {
115 self.tx
116 .as_ref()
117 .map(|tx| tx.size)
118 .expect("only in transaction environment")
119 }
120
121 pub fn tx_auth_info(&self) -> &transaction::AuthInfo {
127 self.tx
128 .as_ref()
129 .map(|tx| &tx.data.auth_info)
130 .expect("only in transaction environment")
131 }
132
133 pub fn tx_call_format(&self) -> transaction::CallFormat {
139 self.tx
140 .as_ref()
141 .map(|tx| tx.data.call.format)
142 .expect("only in transaction environment")
143 }
144
145 pub fn is_read_only(&self) -> bool {
151 self.tx
152 .as_ref()
153 .map(|tx| tx.data.call.read_only)
154 .expect("only in transaction environment")
155 }
156
157 pub fn is_internal(&self) -> bool {
159 self.internal
160 }
161
162 pub fn tx_caller_address(&self) -> Address {
171 self.tx_auth_info()
172 .signer_info
173 .first()
174 .map(|si| si.address_spec.address())
175 .unwrap_or_default()
176 }
177
178 pub fn tx_caller_public_key(&self) -> Option<PublicKey> {
188 self.tx_auth_info()
189 .signer_info
190 .first()
191 .and_then(|si| si.address_spec.public_key())
192 }
193}
194
195#[derive(Clone, Debug)]
197pub struct TransactionWithMeta {
198 pub data: transaction::Transaction,
200 pub size: u32,
202 pub index: usize,
204 pub hash: Hash,
206}
207
208impl TransactionWithMeta {
209 pub fn internal(tx: transaction::Transaction) -> Self {
213 Self {
214 data: tx,
215 size: 0,
216 index: 0,
217 hash: Default::default(),
218 }
219 }
220}
221
222#[cfg(any(test, feature = "test"))]
223impl From<transaction::Transaction> for TransactionWithMeta {
224 fn from(tx: transaction::Transaction) -> Self {
225 Self::internal(tx) }
227}
228
229#[derive(Clone, Default, Debug)]
231pub struct Options {
232 pub mode: Option<Mode>,
233 pub tx: Option<TransactionWithMeta>,
234 pub internal: Option<bool>,
235 pub rng_local_entropy: bool,
236}
237
238impl Options {
239 pub fn new() -> Self {
241 Self::default()
242 }
243
244 pub fn with_mode(self, mode: Mode) -> Self {
246 Self {
247 mode: Some(mode),
248 ..self
249 }
250 }
251
252 pub fn with_tx(self, tx: TransactionWithMeta) -> Self {
254 Self {
255 tx: Some(tx),
256 ..self
257 }
258 }
259
260 pub fn with_internal(self, internal: bool) -> Self {
262 Self {
263 internal: Some(internal),
264 ..self
265 }
266 }
267
268 pub fn with_rng_local_entropy(self) -> Self {
276 Self {
277 rng_local_entropy: true,
278 ..self
279 }
280 }
281}
282
283pub struct State {
288 parent: Option<Box<State>>,
289 store: Option<OverlayStore<Box<dyn Store>>>,
290
291 events: EventTags,
292 unconditional_events: EventTags,
293 messages: Vec<(roothash::Message, MessageEventHookInvocation)>,
294
295 block_values: BTreeMap<&'static str, Box<dyn Any>>,
296 hidden_block_values: Option<BTreeMap<&'static str, Box<dyn Any>>>,
297 local_values: BTreeMap<&'static str, Box<dyn Any>>,
298
299 rng: Option<RootRng>,
300 hidden_rng: Option<RootRng>,
301 env: Environment,
302
303 always_rollback: bool,
304}
305
306impl State {
307 fn init(&mut self, opts: Options) {
309 if let Some(mode) = opts.mode {
310 self.env.mode = mode;
312 if matches!(mode, Mode::PreSchedule | Mode::Simulate) {
315 self.always_rollback = true;
316 self.hide_block_values();
317 }
318 }
319
320 if let Some(tx) = opts.tx {
321 self.rng.as_mut().unwrap().append_tx(tx.hash);
323 self.env.tx = Some(tx);
325 }
326
327 if let Some(internal) = opts.internal {
328 self.env.internal = internal;
329 if internal {
330 self.hide_block_values();
331 }
332 }
333
334 if opts.rng_local_entropy {
335 self.rng.as_mut().unwrap().append_local_entropy();
337 }
338
339 if !matches!(self.env.mode, Mode::PreSchedule) {
340 self.rng.as_mut().unwrap().append_subcontext();
342 } else {
343 self.disable_rng();
345 }
346 }
347
348 pub fn open(&mut self) {
350 let mut parent = Self {
351 parent: None,
352 store: None,
353 events: EventTags::new(),
354 unconditional_events: EventTags::new(),
355 messages: Vec::new(),
356 block_values: BTreeMap::new(),
357 hidden_block_values: None,
358 local_values: BTreeMap::new(),
359 rng: None,
360 hidden_rng: None,
361 env: self.env.clone(),
362 always_rollback: false,
363 };
364 mem::swap(&mut parent, self);
365
366 self.store = parent
368 .store
369 .take()
370 .map(|pstore| OverlayStore::new(Box::new(pstore) as Box<dyn Store>));
371
372 mem::swap(&mut parent.block_values, &mut self.block_values);
374 mem::swap(&mut parent.rng, &mut self.rng);
376
377 self.parent = Some(Box::new(parent));
378 }
379
380 fn convert_store(store: Box<dyn Store>) -> OverlayStore<Box<dyn Store>> {
381 let raw = Box::into_raw(store);
382 unsafe {
383 *Box::from_raw(raw as *mut OverlayStore<Box<dyn Store>>)
385 }
386 }
387
388 pub fn commit(&mut self) {
394 if self.always_rollback {
395 self.rollback();
396 } else {
397 self._commit();
398 }
399 }
400
401 fn _commit(&mut self) {
402 let mut child = *self.parent.take().expect("cannot commit on root state");
403 mem::swap(&mut child, self);
404
405 self.store = child
407 .store
408 .take()
409 .map(|cstore| Self::convert_store(cstore.commit()));
410
411 self.messages.extend(child.messages);
413
414 for (key, event) in child.events {
416 let events = self.events.entry(key).or_default();
417 events.extend(event);
418 }
419 for (key, event) in child.unconditional_events {
420 let events = self.unconditional_events.entry(key).or_default();
421 events.extend(event);
422 }
423
424 if let Some(mut block_values) = child.hidden_block_values {
426 mem::swap(&mut block_values, &mut self.block_values); } else {
428 mem::swap(&mut child.block_values, &mut self.block_values);
429 }
430 if child.hidden_rng.is_some() {
434 mem::swap(&mut child.hidden_rng, &mut self.rng); } else {
436 mem::swap(&mut child.rng, &mut self.rng);
437 }
438 }
439
440 pub fn rollback(&mut self) {
446 let mut child = *self.parent.take().expect("cannot rollback on root state");
447 mem::swap(&mut child, self);
448
449 self.store = child
451 .store
452 .take()
453 .map(|cstore| Self::convert_store(cstore.rollback()));
454
455 if let Some(mut block_values) = child.hidden_block_values {
457 mem::swap(&mut block_values, &mut self.block_values); } else {
459 mem::swap(&mut child.block_values, &mut self.block_values);
460 }
461 if child.hidden_rng.is_some() {
465 mem::swap(&mut child.hidden_rng, &mut self.rng); } else {
467 mem::swap(&mut child.rng, &mut self.rng);
468 }
469 }
470
471 pub fn block_value<V: Any>(&mut self, key: &'static str) -> StateValue<'_, V> {
476 StateValue::new(self.block_values.entry(key))
477 }
478
479 pub fn local_value<V: Any>(&mut self, key: &'static str) -> StateValue<'_, V> {
485 StateValue::new(self.local_values.entry(key))
486 }
487
488 pub fn hide_block_values(&mut self) {
491 if self.parent.is_none() {
492 panic!("cannot hide block values on root state");
494 }
495 if self.hidden_block_values.is_some() {
496 return; }
498
499 self.hidden_block_values = Some(mem::take(&mut self.block_values));
500 }
501
502 pub fn emitted_messages_count(&self) -> usize {
505 self.messages.len()
506 + self
507 .parent
508 .as_ref()
509 .map(|p| p.emitted_messages_count())
510 .unwrap_or_default()
511 }
512
513 pub fn emitted_messages_local_count(&self) -> usize {
516 self.messages.len()
517 }
518
519 pub fn emitted_messages_max<C: Context>(&self, ctx: &C) -> u32 {
521 if self.env.is_transaction() {
522 let limit = self.env.tx_auth_info().fee.consensus_messages;
523 if limit > 0 {
524 limit
525 } else {
526 ctx.max_messages() }
528 } else {
529 ctx.max_messages()
530 }
531 }
532
533 pub fn emit_message<C: Context>(
535 &mut self,
536 ctx: &C,
537 msg: roothash::Message,
538 hook: MessageEventHookInvocation,
539 ) -> Result<(), Error> {
540 if self.emitted_messages_count() >= self.emitted_messages_max(ctx) as usize {
542 return Err(Error::OutOfMessageSlots);
543 }
544
545 self.messages.push((msg, hook));
546
547 Ok(())
548 }
549
550 pub fn take_messages(&mut self) -> Vec<(roothash::Message, MessageEventHookInvocation)> {
552 mem::take(&mut self.messages)
553 }
554
555 pub fn emit_event<E: Event>(&mut self, event: E) {
557 self.emit_event_raw(event.into_event_tag());
558 }
559
560 pub fn emit_event_raw(&mut self, etag: EventTag) {
562 let events = self.events.entry(etag.key).or_default();
563 events.push(etag.value);
564 }
565
566 pub fn emit_unconditional_event<E: Event>(&mut self, event: E) {
570 let etag = event.into_event_tag();
571 let events = self.unconditional_events.entry(etag.key).or_default();
572 events.push(etag.value);
573 }
574
575 pub fn take_events(&mut self) -> EventTags {
577 mem::take(&mut self.events)
578 }
579
580 pub fn take_unconditional_events(&mut self) -> EventTags {
582 mem::take(&mut self.unconditional_events)
583 }
584
585 pub fn take_all_events(&mut self) -> EventTags {
587 let mut events = self.take_events();
588 let unconditional_events = self.take_unconditional_events();
589
590 for (key, val) in unconditional_events {
591 let tag = events.entry(key).or_default();
592 tag.extend(val)
593 }
594
595 events
596 }
597
598 pub fn store(&mut self) -> &mut dyn Store {
604 self.store.as_mut().unwrap()
605 }
606
607 pub fn has_pending_store_updates(&self) -> bool {
609 self.store
610 .as_ref()
611 .map(|store| store.has_pending_updates())
612 .unwrap_or_default()
613 }
614
615 pub fn pending_store_update_byte_size(&self) -> usize {
617 self.store
618 .as_ref()
619 .map(|store| store.pending_update_byte_size())
620 .unwrap_or_default()
621 }
622
623 pub fn rng(&mut self) -> &mut RootRng {
625 self.rng.as_mut().unwrap()
626 }
627
628 fn disable_rng(&mut self) {
630 if self.parent.is_none() {
631 panic!("cannot hide the RNG on root state");
633 }
634 if self.hidden_rng.is_some() {
635 return; }
637
638 self.hidden_rng = mem::replace(&mut self.rng, Some(RootRng::invalid()));
639 }
640
641 pub fn env(&self) -> &Environment {
643 &self.env
644 }
645
646 pub fn env_origin(&self) -> &Environment {
650 match self.parent {
651 Some(ref parent) if self.env.internal => parent.env_origin(),
652 _ => &self.env,
653 }
654 }
655
656 pub fn level(&self) -> usize {
658 if let Some(ref parent) = self.parent {
659 parent.level() + 1
660 } else {
661 0
662 }
663 }
664}
665
666thread_local! {
667 static CURRENT: RefCell<Vec<State>> = const { RefCell::new(Vec::new()) };
668}
669
670struct CurrentStateGuard;
671
672impl Drop for CurrentStateGuard {
673 fn drop(&mut self) {
674 CURRENT.with(|c| {
675 let root = c.borrow_mut().pop().expect("must have current state");
676 let store = root
678 .store
679 .expect("must not have open child states after exiting root state");
680 store.commit();
681 });
682 }
683}
684
685struct TransactionGuard(usize);
686
687impl Drop for TransactionGuard {
688 fn drop(&mut self) {
689 let level = CurrentState::with(|state| state.level());
690
691 if level == self.0 {
693 CurrentState::rollback_transaction();
694 }
695 }
696}
697
698pub enum TransactionResult<T> {
700 Commit(T),
701 Rollback(T),
702}
703
704impl From<()> for TransactionResult<()> {
705 fn from(_: ()) -> TransactionResult<()> {
706 TransactionResult::Commit(())
707 }
708}
709
710impl<R, E> From<Result<R, E>> for TransactionResult<Result<R, E>> {
711 fn from(v: Result<R, E>) -> TransactionResult<Result<R, E>> {
712 match v {
713 Ok(_) => TransactionResult::Commit(v),
714 Err(_) => TransactionResult::Rollback(v),
715 }
716 }
717}
718
719pub struct CurrentState;
721
722impl CurrentState {
723 pub fn enter<S, F, R>(root: S, f: F) -> R
731 where
732 S: Store,
733 F: FnOnce() -> R,
734 {
735 Self::enter_opts(
736 Options {
737 mode: Some(Default::default()), ..Options::default()
739 },
740 root,
741 f,
742 )
743 }
744
745 pub fn enter_opts<S, F, R>(opts: Options, mut root: S, f: F) -> R
754 where
755 S: Store,
756 F: FnOnce() -> R,
757 {
758 let root = unsafe {
759 std::mem::transmute::<&mut dyn Store, &mut (dyn Store + 'static)>(
763 &mut root as &mut dyn Store,
764 )
765 };
766 let mode = opts
768 .mode
769 .expect("mode must be explicitly set on root state");
770 let mut root = State {
771 parent: None,
772 store: Some(OverlayStore::new(Box::new(root) as Box<dyn Store>)),
773 events: EventTags::new(),
774 unconditional_events: EventTags::new(),
775 messages: Vec::new(),
776 block_values: BTreeMap::new(),
777 hidden_block_values: None,
778 local_values: BTreeMap::new(),
779 rng: Some(RootRng::new(mode)),
780 hidden_rng: None,
781 env: Default::default(),
782 always_rollback: false,
783 };
784 root.init(opts);
786
787 CURRENT.with(|c| {
788 c.try_borrow_mut()
789 .expect("must not re-enter from with block")
790 .push(root)
791 });
792 let _guard = CurrentStateGuard; f()
795 }
796
797 #[doc(hidden)]
806 pub(crate) fn init_local_fallback() {
807 thread_local! {
808 static BASE_STATE_INIT: RefCell<bool> = const { RefCell::new(false) };
809 }
810
811 BASE_STATE_INIT.with(|initialized| {
812 if *initialized.borrow() {
814 return;
815 }
816 *initialized.borrow_mut() = true;
817
818 let root = mkvs::OverlayTree::new(
819 mkvs::Tree::builder()
820 .with_root_type(mkvs::RootType::State)
821 .build(Box::new(mkvs::sync::NoopReadSyncer)),
822 );
823 let root = MKVSStore::new(root);
824
825 let root = State {
827 parent: None,
828 store: Some(OverlayStore::new(Box::new(root) as Box<dyn Store>)),
829 events: EventTags::new(),
830 unconditional_events: EventTags::new(),
831 messages: Vec::new(),
832 block_values: BTreeMap::new(),
833 hidden_block_values: None,
834 local_values: BTreeMap::new(),
835 rng: Some(RootRng::new(Default::default())),
836 hidden_rng: None,
837 env: Default::default(),
838 always_rollback: false,
839 };
840
841 CURRENT.with(|c| {
842 let mut current = c
843 .try_borrow_mut()
844 .expect("must not re-enter from with block");
845 assert!(
846 current.is_empty(),
847 "must have no prior states attached to local thread"
848 );
849
850 current.push(root);
851 });
852 });
853 }
854
855 pub fn with<F, R>(f: F) -> R
862 where
863 F: FnOnce(&mut State) -> R,
864 {
865 CURRENT.with(|c| {
866 let mut current_ref = c.try_borrow_mut().expect("must not re-enter with");
867 let current = current_ref.last_mut().expect("must enter context");
868
869 f(current)
870 })
871 }
872
873 pub fn with_store<F, R>(f: F) -> R
880 where
881 F: FnOnce(&mut dyn Store) -> R,
882 {
883 Self::with(|state| f(state.store()))
884 }
885
886 pub fn with_env<F, R>(f: F) -> R
893 where
894 F: FnOnce(&Environment) -> R,
895 {
896 Self::with(|state| f(state.env()))
897 }
898
899 pub fn with_env_origin<F, R>(f: F) -> R
906 where
907 F: FnOnce(&Environment) -> R,
908 {
909 Self::with(|state| f(state.env_origin()))
910 }
911
912 pub fn start_transaction() {
919 Self::with(|state| state.open());
920 }
921
922 pub fn commit_transaction() {
930 Self::with(|state| state.commit());
931 }
932
933 pub fn rollback_transaction() {
941 Self::with(|state| state.rollback());
942 }
943
944 pub fn with_transaction<F, R, Rs>(f: F) -> R
949 where
950 F: FnOnce() -> Rs,
951 Rs: Into<TransactionResult<R>>,
952 {
953 Self::with_transaction_opts(Options::default(), f)
954 }
955
956 pub fn with_transaction_opts<F, R, Rs>(opts: Options, f: F) -> R
961 where
962 F: FnOnce() -> Rs,
963 Rs: Into<TransactionResult<R>>,
964 {
965 let level = Self::with(|state| {
966 state.open();
967 state.init(opts);
968 state.level()
969 });
970 let _guard = TransactionGuard(level); match f().into() {
973 TransactionResult::Commit(result) => {
974 Self::commit_transaction();
975 result
976 }
977 TransactionResult::Rollback(result) => {
978 Self::rollback_transaction();
979 result
980 }
981 }
982 }
983}
984
985pub struct StateValue<'a, V> {
987 inner: Entry<'a, &'static str, Box<dyn Any>>,
988 _value: PhantomData<V>,
989}
990
991impl<'a, V: Any> StateValue<'a, V> {
992 fn new(inner: Entry<'a, &'static str, Box<dyn Any>>) -> Self {
993 Self {
994 inner,
995 _value: PhantomData,
996 }
997 }
998
999 pub fn get(self) -> Option<&'a V> {
1005 match self.inner {
1006 Entry::Occupied(oe) => Some(
1007 oe.into_mut()
1008 .downcast_ref()
1009 .expect("type should stay the same"),
1010 ),
1011 _ => None,
1012 }
1013 }
1014
1015 pub fn get_mut(&mut self) -> Option<&mut V> {
1021 match &mut self.inner {
1022 Entry::Occupied(oe) => Some(
1023 oe.get_mut()
1024 .downcast_mut()
1025 .expect("type should stay the same"),
1026 ),
1027 _ => None,
1028 }
1029 }
1030
1031 pub fn set(self, value: V) -> &'a mut V {
1037 let value = Box::new(value);
1038 match self.inner {
1039 Entry::Occupied(mut oe) => {
1040 oe.insert(value);
1041 oe.into_mut()
1042 }
1043 Entry::Vacant(ve) => ve.insert(value),
1044 }
1045 .downcast_mut()
1046 .expect("type should stay the same")
1047 }
1048
1049 pub fn take(self) -> Option<V> {
1055 match self.inner {
1056 Entry::Occupied(oe) => {
1057 Some(*oe.remove().downcast().expect("type should stay the same"))
1058 }
1059 Entry::Vacant(_) => None,
1060 }
1061 }
1062}
1063
1064impl<'a, V: Any + Default> StateValue<'a, V> {
1065 pub fn or_default(self) -> &'a mut V {
1071 match self.inner {
1072 Entry::Occupied(oe) => oe.into_mut(),
1073 Entry::Vacant(ve) => ve.insert(Box::<V>::default()),
1074 }
1075 .downcast_mut()
1076 .expect("type should stay the same")
1077 }
1078}
1079
1080#[cfg(test)]
1081mod test {
1082 use oasis_core_runtime::{
1083 common::versioned::Versioned,
1084 consensus::{roothash, staking},
1085 storage::mkvs,
1086 };
1087
1088 use super::{CurrentState, Mode, Options, TransactionResult, TransactionWithMeta};
1089 use crate::{
1090 modules::core::Event,
1091 storage::{MKVSStore, Store},
1092 testing::mock::{self, Mock},
1093 types::message::MessageEventHookInvocation,
1094 };
1095
1096 #[test]
1097 fn test_value() {
1098 CurrentState::init_local_fallback();
1099
1100 CurrentState::with(|state| {
1101 let x = state.block_value::<u64>("module.TestKey").get();
1102 assert_eq!(x, None);
1103
1104 state.block_value::<u64>("module.TestKey").set(42);
1105
1106 let y = state.block_value::<u64>("module.TestKey").get();
1107 assert_eq!(y, Some(&42u64));
1108
1109 let z = state.block_value::<u64>("module.TestKey").take();
1110 assert_eq!(z, Some(42u64));
1111
1112 let y = state.block_value::<u64>("module.TestKey").get();
1113 assert_eq!(y, None);
1114 });
1115 }
1116
1117 #[test]
1118 #[should_panic]
1119 fn test_value_type_change_block_value() {
1120 CurrentState::init_local_fallback();
1121
1122 CurrentState::with(|state| {
1123 state.block_value::<u64>("module.TestKey").or_default();
1124 state.block_value::<u32>("module.TestKey").get();
1125 });
1126 }
1127
1128 #[test]
1129 #[should_panic]
1130 fn test_value_type_change_local_value() {
1131 CurrentState::init_local_fallback();
1132
1133 CurrentState::with(|state| {
1134 state.local_value::<u64>("module.TestKey").or_default();
1135 state.local_value::<u32>("module.TestKey").get();
1136 });
1137 }
1138
1139 #[test]
1140 fn test_value_hidden_block_values() {
1141 CurrentState::init_local_fallback();
1142
1143 CurrentState::with(|state| {
1144 state.block_value("module.TestKey").set(42u64);
1145
1146 state.open();
1147 state.hide_block_values();
1148
1149 let v = state.block_value::<u64>("module.TestKey").get();
1150 assert!(v.is_none(), "block values should not propagate when hidden");
1151
1152 state.block_value("module.TestKey").set(48u64);
1153
1154 state.commit();
1155
1156 let v = state.block_value::<u64>("module.TestKey").get();
1157 assert_eq!(
1158 v,
1159 Some(&42u64),
1160 "block values should not propagate when hidden"
1161 );
1162 });
1163 }
1164
1165 #[test]
1166 fn test_value_local() {
1167 CurrentState::init_local_fallback();
1168
1169 CurrentState::with(|state| {
1170 state.block_value("module.TestKey").set(42u64);
1171
1172 state.open();
1173
1174 let mut y = state.block_value::<u64>("module.TestKey");
1175 let y = y.get_mut().unwrap();
1176 assert_eq!(*y, 42);
1177 *y = 48;
1178
1179 let a = state.local_value::<u64>("module.TestTxKey").get();
1180 assert_eq!(a, None);
1181 state.local_value::<u64>("module.TestTxKey").set(65);
1182
1183 let b = state.local_value::<u64>("module.TestTxKey").get();
1184 assert_eq!(b, Some(&65));
1185
1186 let c = state
1187 .local_value::<u64>("module.TestTakeTxKey")
1188 .or_default();
1189 *c = 67;
1190 let d = state.local_value::<u64>("module.TestTakeTxKey").take();
1191 assert_eq!(d, Some(67));
1192 let e = state.local_value::<u64>("module.TestTakeTxKey").get();
1193 assert_eq!(e, None);
1194
1195 state.rollback(); let x = state.block_value::<u64>("module.TestKey").get();
1198 assert_eq!(x, Some(&48));
1199
1200 state.open();
1201
1202 let z = state.block_value::<u64>("module.TestKey").take();
1203 assert_eq!(z, Some(48));
1204
1205 let a = state.local_value::<u64>("module.TestTxKey").get();
1206 assert_eq!(a, None, "local values should not be propagated");
1207
1208 state.rollback(); let y = state.block_value::<u64>("module.TestKey").get();
1211 assert_eq!(y, None);
1212 });
1213 }
1214
1215 #[test]
1216 fn test_emit_messages() {
1217 let mut mock = Mock::default(); let max_messages = mock.max_messages as usize;
1219 let ctx = mock.create_ctx();
1220
1221 CurrentState::with(|state| {
1222 state.open();
1223
1224 assert_eq!(state.emitted_messages_count(), 0);
1225 assert_eq!(state.emitted_messages_local_count(), 0);
1226
1227 state
1228 .emit_message(
1229 &ctx,
1230 roothash::Message::Staking(Versioned::new(
1231 0,
1232 roothash::StakingMessage::Transfer(staking::Transfer::default()),
1233 )),
1234 MessageEventHookInvocation::new("test".to_string(), ""),
1235 )
1236 .expect("message emission should succeed");
1237 assert_eq!(state.emitted_messages_count(), 1);
1238 assert_eq!(state.emitted_messages_local_count(), 1);
1239 assert_eq!(state.emitted_messages_max(&ctx), max_messages as u32);
1240
1241 state.open(); assert_eq!(state.emitted_messages_local_count(), 0);
1244
1245 state
1246 .emit_message(
1247 &ctx,
1248 roothash::Message::Staking(Versioned::new(
1249 0,
1250 roothash::StakingMessage::Transfer(staking::Transfer::default()),
1251 )),
1252 MessageEventHookInvocation::new("test".to_string(), ""),
1253 )
1254 .expect("message emission should succeed");
1255 assert_eq!(state.emitted_messages_count(), 2);
1256 assert_eq!(state.emitted_messages_local_count(), 1);
1257 assert_eq!(state.emitted_messages_max(&ctx), max_messages as u32);
1258
1259 state.rollback(); assert_eq!(
1262 state.emitted_messages_count(),
1263 1,
1264 "emitted message should have been rolled back"
1265 );
1266 assert_eq!(state.emitted_messages_local_count(), 1);
1267
1268 state.open(); assert_eq!(state.emitted_messages_local_count(), 0);
1271
1272 state
1273 .emit_message(
1274 &ctx,
1275 roothash::Message::Staking(Versioned::new(
1276 0,
1277 roothash::StakingMessage::Transfer(staking::Transfer::default()),
1278 )),
1279 MessageEventHookInvocation::new("test".to_string(), ""),
1280 )
1281 .expect("message emission should succeed");
1282 assert_eq!(state.emitted_messages_count(), 2);
1283 assert_eq!(state.emitted_messages_local_count(), 1);
1284
1285 state.commit(); assert_eq!(
1288 state.emitted_messages_count(),
1289 2,
1290 "emitted message should have been committed"
1291 );
1292
1293 for _ in 0..max_messages - 2 {
1295 state
1296 .emit_message(
1297 &ctx,
1298 roothash::Message::Staking(Versioned::new(
1299 0,
1300 roothash::StakingMessage::Transfer(staking::Transfer::default()),
1301 )),
1302 MessageEventHookInvocation::new("test".to_string(), ""),
1303 )
1304 .expect("message emission should succeed");
1305 }
1306 assert_eq!(state.emitted_messages_count(), max_messages);
1307
1308 state
1310 .emit_message(
1311 &ctx,
1312 roothash::Message::Staking(Versioned::new(
1313 0,
1314 roothash::StakingMessage::Transfer(staking::Transfer::default()),
1315 )),
1316 MessageEventHookInvocation::new("test".to_string(), ""),
1317 )
1318 .expect_err("message emission should fail due to out of slots");
1319 assert_eq!(state.emitted_messages_count(), max_messages);
1320
1321 state.rollback(); assert_eq!(state.emitted_messages_count(), 0);
1324 });
1325
1326 let mut tx = mock::transaction();
1328 tx.auth_info.fee.consensus_messages = 1; CurrentState::with_transaction_opts(Options::new().with_tx(tx.into()), || {
1330 CurrentState::with(|state| {
1331 assert_eq!(state.emitted_messages_max(&ctx), 1);
1332 });
1333 });
1334
1335 let mut tx = mock::transaction();
1336 tx.auth_info.fee.consensus_messages = 0; CurrentState::with_transaction_opts(Options::new().with_tx(tx.into()), || {
1338 CurrentState::with(|state| {
1339 assert_eq!(state.emitted_messages_max(&ctx), max_messages as u32);
1340 });
1341 });
1342 }
1343
1344 #[test]
1345 fn test_emit_events() {
1346 CurrentState::init_local_fallback();
1347
1348 CurrentState::with(|state| {
1349 state.open();
1350
1351 state.open();
1352
1353 state.emit_event(Event::GasUsed { amount: 41 });
1354 state.emit_event(Event::GasUsed { amount: 42 });
1355 state.emit_event(Event::GasUsed { amount: 43 });
1356
1357 state.emit_unconditional_event(Event::GasUsed { amount: 10 });
1358
1359 state.commit();
1360
1361 let events = state.take_events();
1362 assert_eq!(events.len(), 1, "events should have been propagated");
1363 let event_key = b"core\x00\x00\x00\x01".to_vec();
1364 assert_eq!(events[&event_key].len(), 3);
1365
1366 let events = state.take_unconditional_events();
1367 assert_eq!(
1368 events.len(),
1369 1,
1370 "unconditional events should have been propagated"
1371 );
1372 let event_key = b"core\x00\x00\x00\x01".to_vec();
1373 assert_eq!(events[&event_key].len(), 1);
1374
1375 state.emit_event(Event::GasUsed { amount: 41 });
1376 state.emit_event(Event::GasUsed { amount: 42 });
1377 state.emit_event(Event::GasUsed { amount: 43 });
1378
1379 state.emit_unconditional_event(Event::GasUsed { amount: 20 });
1380
1381 state.rollback();
1382
1383 let events = state.take_events();
1384 assert_eq!(events.len(), 0, "events should not have been propagated");
1385
1386 let events = state.take_unconditional_events();
1387 assert_eq!(
1388 events.len(),
1389 0,
1390 "unconditional events should not have been propagated"
1391 );
1392 });
1393 }
1394
1395 fn test_store_basic() {
1396 CurrentState::start_transaction();
1397
1398 assert!(
1399 !CurrentState::with(|state| state.has_pending_store_updates()),
1400 "should not have pending updates"
1401 );
1402
1403 CurrentState::with_store(|store| {
1404 store.insert(b"test", b"value");
1405 });
1406
1407 assert!(
1408 CurrentState::with(|state| state.has_pending_store_updates()),
1409 "should have pending updates after insert"
1410 );
1411
1412 CurrentState::with_transaction(|| {
1414 assert!(
1415 !CurrentState::with(|state| state.has_pending_store_updates()),
1416 "should not have pending updates"
1417 );
1418
1419 CurrentState::with_store(|store| {
1420 store.insert(b"test", b"b0rken");
1421 });
1422
1423 assert!(
1424 CurrentState::with(|state| state.has_pending_store_updates()),
1425 "should have pending updates after insert"
1426 );
1427
1428 TransactionResult::Rollback(())
1429 });
1430
1431 CurrentState::with_transaction_opts(
1433 Options::new()
1434 .with_mode(Mode::Check)
1435 .with_internal(true)
1436 .with_tx(TransactionWithMeta {
1437 data: mock::transaction(),
1438 size: 888,
1439 index: 42,
1440 hash: Default::default(),
1441 }),
1442 || {
1443 CurrentState::with_env(|env| {
1444 assert!(env.is_check_only(), "environment should be updated");
1445 assert!(env.is_internal(), "environment should be updated");
1446 assert!(env.is_transaction(), "environment should be updated");
1447 assert_eq!(env.tx_index(), 42, "environment should be updated");
1448 assert_eq!(env.tx_size(), 888, "environment should be updated");
1449 });
1450
1451 CurrentState::with_env_origin(|env_origin| {
1452 assert!(
1453 !env_origin.is_check_only(),
1454 "origin environment should be correct"
1455 );
1456 assert!(
1457 !env_origin.is_transaction(),
1458 "origin environment should be correct"
1459 );
1460 });
1461
1462 CurrentState::with_transaction(|| {
1463 CurrentState::with_env(|env| {
1465 assert!(env.is_check_only(), "environment should propagate");
1466 assert!(env.is_internal(), "environment should propagate");
1467 assert!(env.is_transaction(), "environment should propagate");
1468 assert_eq!(env.tx_index(), 42, "environment should propagate");
1469 assert_eq!(env.tx_size(), 888, "environment should propagate");
1470 });
1471
1472 TransactionResult::Rollback(())
1473 });
1474
1475 TransactionResult::Rollback(())
1476 },
1477 );
1478
1479 CurrentState::with_env(|env| {
1480 assert!(!env.is_transaction(), "environment should not leak");
1481 });
1482
1483 let unrelated = mkvs::OverlayTree::new(
1485 mkvs::Tree::builder()
1486 .with_root_type(mkvs::RootType::State)
1487 .build(Box::new(mkvs::sync::NoopReadSyncer)),
1488 );
1489 let mut unrelated = MKVSStore::new(unrelated);
1490
1491 CurrentState::enter(&mut unrelated, || {
1492 CurrentState::start_transaction();
1493
1494 CurrentState::with_store(|store| {
1495 store.insert(b"test", b"should not touch the original root");
1496 });
1497
1498 CurrentState::commit_transaction();
1499 });
1500
1501 CurrentState::with_store(|store| {
1502 store.insert(b"another", b"value 2");
1503 });
1504
1505 CurrentState::commit_transaction();
1506 }
1507
1508 #[test]
1509 fn test_basic() {
1510 let root = mkvs::OverlayTree::new(
1511 mkvs::Tree::builder()
1512 .with_root_type(mkvs::RootType::State)
1513 .build(Box::new(mkvs::sync::NoopReadSyncer)),
1514 );
1515 let mut root = MKVSStore::new(root);
1516
1517 CurrentState::enter(&mut root, || {
1518 test_store_basic();
1519 });
1520
1521 let value = root.get(b"test").unwrap();
1522 assert_eq!(value, b"value");
1523 }
1524
1525 #[test]
1526 fn test_local_fallback() {
1527 CurrentState::init_local_fallback();
1529 CurrentState::init_local_fallback(); test_store_basic();
1533
1534 CurrentState::with_store(|store| {
1535 let value = store.get(b"test").unwrap();
1536 assert_eq!(value, b"value");
1537 });
1538
1539 let root = mkvs::OverlayTree::new(
1541 mkvs::Tree::builder()
1542 .with_root_type(mkvs::RootType::State)
1543 .build(Box::new(mkvs::sync::NoopReadSyncer)),
1544 );
1545 let mut root = MKVSStore::new(root);
1546
1547 CurrentState::enter(&mut root, || {
1548 CurrentState::with_store(|store| {
1549 assert!(store.get(b"test").is_none(), "store should be empty");
1550 store.insert(b"unrelated", b"unrelated");
1551 });
1552
1553 test_store_basic();
1554 });
1555
1556 let value = root.get(b"test").unwrap();
1557 assert_eq!(value, b"value");
1558 let value = root.get(b"unrelated").unwrap();
1559 assert_eq!(value, b"unrelated");
1560
1561 CurrentState::with_store(|store| {
1563 assert!(store.get(b"unrelated").is_none(), "changes should not leak");
1564 });
1565 }
1566
1567 #[test]
1568 #[should_panic(expected = "must enter context")]
1569 fn test_fail_not_entered() {
1570 test_store_basic(); }
1572
1573 #[test]
1574 #[should_panic(expected = "must not re-enter with")]
1575 fn test_fail_reenter_with() {
1576 CurrentState::init_local_fallback();
1577
1578 CurrentState::with(|_| {
1579 CurrentState::with(|_| {
1580 });
1582 });
1583 }
1584
1585 #[test]
1586 #[should_panic(expected = "must not re-enter with")]
1587 fn test_fail_reenter_with_start_transaction() {
1588 CurrentState::init_local_fallback();
1589
1590 CurrentState::with(|_| {
1591 CurrentState::start_transaction(); });
1593 }
1594
1595 #[test]
1596 #[should_panic(expected = "must not re-enter with")]
1597 fn test_fail_reenter_with_commit_transaction() {
1598 CurrentState::init_local_fallback();
1599
1600 CurrentState::with(|_| {
1601 CurrentState::commit_transaction(); });
1603 }
1604
1605 #[test]
1606 #[should_panic(expected = "must not re-enter with")]
1607 fn test_fail_reenter_with_rollback_transaction() {
1608 CurrentState::init_local_fallback();
1609
1610 CurrentState::with(|_| {
1611 CurrentState::rollback_transaction(); });
1613 }
1614
1615 #[test]
1616 #[should_panic(expected = "must not re-enter from with block")]
1617 fn test_fail_reenter_with_enter() {
1618 CurrentState::init_local_fallback();
1619
1620 CurrentState::with(|_| {
1621 let unrelated = mkvs::OverlayTree::new(
1622 mkvs::Tree::builder()
1623 .with_root_type(mkvs::RootType::State)
1624 .build(Box::new(mkvs::sync::NoopReadSyncer)),
1625 );
1626 let mut unrelated = MKVSStore::new(unrelated);
1627
1628 CurrentState::enter(&mut unrelated, || {
1629 });
1631 });
1632 }
1633
1634 #[test]
1635 #[should_panic(expected = "must not re-enter from with block")]
1636 fn test_fail_local_fallback_within_with() {
1637 let root = mkvs::OverlayTree::new(
1638 mkvs::Tree::builder()
1639 .with_root_type(mkvs::RootType::State)
1640 .build(Box::new(mkvs::sync::NoopReadSyncer)),
1641 );
1642 let mut root = MKVSStore::new(root);
1643
1644 CurrentState::enter(&mut root, || {
1645 CurrentState::with(|_| {
1646 CurrentState::init_local_fallback(); })
1648 });
1649 }
1650
1651 #[test]
1652 #[should_panic(expected = "must have no prior states attached to local thread")]
1653 fn test_fail_local_fallback_within_enter() {
1654 let root = mkvs::OverlayTree::new(
1655 mkvs::Tree::builder()
1656 .with_root_type(mkvs::RootType::State)
1657 .build(Box::new(mkvs::sync::NoopReadSyncer)),
1658 );
1659 let mut root = MKVSStore::new(root);
1660
1661 CurrentState::enter(&mut root, || {
1662 CurrentState::init_local_fallback(); });
1664 }
1665
1666 #[test]
1667 #[should_panic(expected = "cannot commit on root state")]
1668 fn test_fail_commit_transaction_must_exist() {
1669 CurrentState::init_local_fallback();
1670
1671 CurrentState::commit_transaction(); }
1673
1674 #[test]
1675 #[should_panic(expected = "cannot rollback on root state")]
1676 fn test_fail_rollback_transaction_must_exist() {
1677 CurrentState::init_local_fallback();
1678
1679 CurrentState::rollback_transaction(); }
1681}