1use super::{casper_types::U512, CallDef};
8use crate::prelude::*;
9
10#[derive(Clone, Debug, PartialEq, Eq)]
12pub enum CallstackElement {
13 Account(Address),
15 ContractCall {
17 address: Address,
19 call_def: CallDef
21 }
22}
23
24impl CallstackElement {
25 pub fn new_account(address: Address) -> Self {
27 Self::Account(address)
28 }
29
30 pub fn new_contract_call(address: Address, call_def: CallDef) -> Self {
32 Self::ContractCall { address, call_def }
33 }
34}
35
36impl CallstackElement {
37 pub fn address(&self) -> &Address {
39 match self {
40 CallstackElement::Account(address) => address,
41 CallstackElement::ContractCall { address, .. } => address
42 }
43 }
44}
45
46#[derive(Clone, Default)]
48pub struct Callstack(Vec<CallstackElement>);
49
50impl Callstack {
51 pub fn first(&self) -> CallstackElement {
53 self.0
54 .first()
55 .expect("Not enough elements on callstack")
56 .clone()
57 }
58
59 pub fn pop(&mut self) -> Option<CallstackElement> {
61 self.0.pop()
62 }
63
64 pub fn push(&mut self, element: CallstackElement) {
66 self.0.push(element);
67 }
68
69 pub fn attached_value(&self) -> U512 {
75 let ce = self.0.last().expect("Not enough elements on callstack");
76 match ce {
77 CallstackElement::Account(_) => U512::zero(),
78 CallstackElement::ContractCall { call_def, .. } => call_def.amount()
79 }
80 }
81
82 pub fn attach_value(&mut self, amount: U512) {
86 if let Some(CallstackElement::ContractCall { call_def, .. }) = self.0.last_mut() {
87 *call_def = call_def.clone().with_amount(amount);
88 }
89 }
90
91 pub fn current(&self) -> &CallstackElement {
93 self.0.last().expect("Not enough elements on callstack")
94 }
95
96 pub fn previous(&self) -> &CallstackElement {
98 self.0
99 .get(self.0.len() - 2)
100 .expect("Not enough elements on callstack")
101 }
102
103 pub fn size(&self) -> usize {
105 self.0.len()
106 }
107
108 pub fn is_empty(&self) -> bool {
110 self.0.is_empty()
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use casper_types::{account::AccountHash, RuntimeArgs};
117
118 use super::*;
119
120 #[test]
121 fn test_first() {
122 let mut callstack = Callstack::default();
123 callstack.push(mock_account_element());
124 callstack.push(mock_contract_element());
125
126 assert_eq!(callstack.first(), mock_account_element());
127 }
128
129 #[test]
130 fn test_pop() {
131 let mut callstack = Callstack::default();
132 callstack.push(mock_account_element());
133 callstack.push(mock_contract_element());
134
135 assert_eq!(callstack.pop(), Some(mock_contract_element()));
136 }
137
138 #[test]
139 fn test_push() {
140 let mut callstack = Callstack::default();
141 callstack.push(mock_account_element());
142 callstack.push(mock_contract_element());
143
144 assert_eq!(callstack.size(), 2);
145 }
146
147 #[test]
148 fn test_attached_value() {
149 let mut callstack = Callstack::default();
150 callstack.push(mock_account_element());
151 assert_eq!(callstack.attached_value(), U512::zero());
152
153 callstack.push(mock_contract_element_with_value(U512::from(100)));
154 assert_eq!(callstack.attached_value(), U512::from(100));
155 }
156
157 #[test]
158 fn test_attach_value() {
159 let mut callstack = Callstack::default();
160 callstack.push(mock_account_element());
161 callstack.push(mock_contract_element());
162
163 callstack.attach_value(U512::from(200));
164
165 assert_eq!(
166 callstack.current(),
167 &mock_contract_element_with_value(U512::from(200))
168 );
169 }
170
171 #[test]
172 fn test_previous() {
173 let mut callstack = Callstack::default();
174 callstack.push(mock_account_element());
175 callstack.push(mock_contract_element());
176
177 assert_eq!(callstack.previous(), &mock_account_element());
178 }
179
180 #[test]
181 fn test_size() {
182 let mut callstack = Callstack::default();
183
184 callstack.push(mock_account_element());
185 callstack.push(mock_contract_element());
186
187 assert_eq!(callstack.size(), 2);
188 }
189
190 #[test]
191 fn test_is_empty() {
192 let mut callstack = Callstack::default();
193 assert!(callstack.is_empty());
194
195 callstack.push(mock_account_element());
196 assert!(!callstack.is_empty());
197 }
198
199 const PACKAGE_HASH: &str =
200 "package-7ba9daac84bebee8111c186588f21ebca35550b6cf1244e71768bd871938be6a";
201 const ACCOUNT_HASH: &str =
202 "account-hash-3b4ffcfb21411ced5fc1560c3f6ffed86f4885e5ea05cde49d90962a48a14d95";
203
204 fn mock_account_element() -> CallstackElement {
205 CallstackElement::Account(Address::Account(
206 AccountHash::from_formatted_str(ACCOUNT_HASH).unwrap()
207 ))
208 }
209
210 fn mock_contract_element() -> CallstackElement {
211 CallstackElement::new_contract_call(
212 Address::new(PACKAGE_HASH).unwrap(),
213 CallDef::new("a", false, RuntimeArgs::default())
214 )
215 }
216
217 fn mock_contract_element_with_value(amount: U512) -> CallstackElement {
218 CallstackElement::new_contract_call(
219 Address::new(PACKAGE_HASH).unwrap(),
220 CallDef::new("a", false, RuntimeArgs::default()).with_amount(amount)
221 )
222 }
223}