casper_execution_engine/runtime/
stack.rs1use casper_types::{account::AccountHash, system::Caller};
3
4pub type RuntimeStackFrame = Caller;
11
12#[derive(Clone, Debug)]
14pub struct RuntimeStack {
15 frames: Vec<RuntimeStackFrame>,
16 max_height: usize,
17}
18
19#[cfg(test)]
21#[derive(Debug)]
22struct RuntimeStackUnderflow;
23
24#[derive(Debug)]
26pub struct RuntimeStackOverflow;
27
28impl RuntimeStack {
29 pub fn new(max_height: usize) -> Self {
31 Self {
32 frames: Vec::with_capacity(max_height),
33 max_height,
34 }
35 }
36
37 pub fn new_with_frame(max_height: usize, frame: RuntimeStackFrame) -> Self {
39 let mut frames = Vec::with_capacity(max_height);
40 frames.push(frame);
41 Self { frames, max_height }
42 }
43
44 pub fn is_empty(&self) -> bool {
46 self.frames.is_empty()
47 }
48
49 pub fn len(&self) -> usize {
51 self.frames.len()
52 }
53
54 pub fn current_frame(&self) -> Option<&RuntimeStackFrame> {
56 self.frames.last()
57 }
58
59 pub fn previous_frame(&self) -> Option<&RuntimeStackFrame> {
61 self.frames.iter().nth_back(1)
62 }
63
64 pub fn first_frame(&self) -> Option<&RuntimeStackFrame> {
66 self.frames.first()
67 }
68
69 #[cfg(test)]
71 fn pop(&mut self) -> Result<(), RuntimeStackUnderflow> {
72 self.frames.pop().ok_or(RuntimeStackUnderflow)?;
73 Ok(())
74 }
75
76 pub fn push(&mut self, frame: RuntimeStackFrame) -> Result<(), RuntimeStackOverflow> {
78 if self.len() < self.max_height {
79 self.frames.push(frame);
80 Ok(())
81 } else {
82 Err(RuntimeStackOverflow)
83 }
84 }
85
86 pub fn call_stack_elements(&self) -> &Vec<Caller> {
89 &self.frames
90 }
91
92 pub fn from_account_hash(account_hash: AccountHash, max_height: usize) -> Self {
94 RuntimeStack {
95 frames: vec![Caller::initiator(account_hash)],
96 max_height,
97 }
98 }
99}
100
101#[cfg(test)]
102mod test {
103 use core::convert::TryInto;
104
105 use casper_types::account::{AccountHash, ACCOUNT_HASH_LENGTH};
106
107 use super::*;
108
109 fn nth_frame(n: usize) -> Caller {
110 let mut bytes = [0_u8; ACCOUNT_HASH_LENGTH];
111 let n: u32 = n.try_into().unwrap();
112 bytes[0..4].copy_from_slice(&n.to_le_bytes());
113 Caller::initiator(AccountHash::new(bytes))
114 }
115
116 #[allow(clippy::redundant_clone)]
117 #[test]
118 fn stack_should_respect_max_height_after_clone() {
119 const MAX_HEIGHT: usize = 3;
120 let mut stack = RuntimeStack::new(MAX_HEIGHT);
121 stack.push(nth_frame(1)).unwrap();
122
123 let mut stack2 = stack.clone();
124 stack2.push(nth_frame(2)).unwrap();
125 stack2.push(nth_frame(3)).unwrap();
126 stack2.push(nth_frame(4)).unwrap_err();
127 assert_eq!(stack2.len(), MAX_HEIGHT);
128 }
129
130 #[test]
131 fn stack_should_work_as_expected() {
132 const MAX_HEIGHT: usize = 6;
133
134 let mut stack = RuntimeStack::new(MAX_HEIGHT);
135 assert!(stack.is_empty());
136 assert_eq!(stack.len(), 0);
137 assert_eq!(stack.current_frame(), None);
138 assert_eq!(stack.previous_frame(), None);
139 assert_eq!(stack.first_frame(), None);
140
141 stack.push(nth_frame(0)).unwrap();
142 assert!(!stack.is_empty());
143 assert_eq!(stack.len(), 1);
144 assert_eq!(stack.current_frame(), Some(&nth_frame(0)));
145 assert_eq!(stack.previous_frame(), None);
146 assert_eq!(stack.first_frame(), Some(&nth_frame(0)));
147
148 let mut n: usize = 1;
149 while stack.push(nth_frame(n)).is_ok() {
150 n += 1;
151 assert!(!stack.is_empty());
152 assert_eq!(stack.len(), n);
153 assert_eq!(stack.current_frame(), Some(&nth_frame(n - 1)));
154 assert_eq!(stack.previous_frame(), Some(&nth_frame(n - 2)));
155 assert_eq!(stack.first_frame(), Some(&nth_frame(0)));
156 }
157 assert!(!stack.is_empty());
158 assert_eq!(stack.len(), MAX_HEIGHT);
159 assert_eq!(stack.current_frame(), Some(&nth_frame(MAX_HEIGHT - 1)));
160 assert_eq!(stack.previous_frame(), Some(&nth_frame(MAX_HEIGHT - 2)));
161 assert_eq!(stack.first_frame(), Some(&nth_frame(0)));
162
163 while stack.len() >= 3 {
164 stack.pop().unwrap();
165 n = n.checked_sub(1).unwrap();
166 assert!(!stack.is_empty());
167 assert_eq!(stack.len(), n);
168 assert_eq!(stack.current_frame(), Some(&nth_frame(n - 1)));
169 assert_eq!(stack.previous_frame(), Some(&nth_frame(n - 2)));
170 assert_eq!(stack.first_frame(), Some(&nth_frame(0)));
171 }
172
173 stack.pop().unwrap();
174 assert!(!stack.is_empty());
175 assert_eq!(stack.len(), 1);
176 assert_eq!(stack.current_frame(), Some(&nth_frame(0)));
177 assert_eq!(stack.previous_frame(), None);
178 assert_eq!(stack.first_frame(), Some(&nth_frame(0)));
179
180 stack.pop().unwrap();
181 assert!(stack.is_empty());
182 assert_eq!(stack.len(), 0);
183 assert_eq!(stack.current_frame(), None);
184 assert_eq!(stack.previous_frame(), None);
185 assert_eq!(stack.first_frame(), None);
186
187 assert!(stack.pop().is_err());
188 }
189}