1use crate::{Access, Op, Stack, StateRead, ToOpcode};
2use bitflags::bitflags;
3
4#[derive(Debug, Copy, Clone, PartialEq, Eq)]
6pub struct Effects(u8);
7
8bitflags! {
9 impl Effects: u8 {
10 const KeyRange = 1 << 0;
12 const KeyRangeExtern = 1 << 1;
14 const ThisAddress = 1 << 2;
16 const ThisContractAddress = 1 << 3;
18 const PostKeyRange = 1 << 4;
20 const PostKeyRangeExtern = 1 << 5;
22 }
23}
24
25pub fn analyze(ops: &[Op]) -> Effects {
27 let mut effects = Effects::empty();
28
29 for op in ops {
30 match op {
31 Op::StateRead(StateRead::KeyRangeExtern) => effects |= Effects::KeyRangeExtern,
32 Op::StateRead(StateRead::KeyRange) => effects |= Effects::KeyRange,
33 Op::Access(Access::ThisAddress) => effects |= Effects::ThisAddress,
34 Op::Access(Access::ThisContractAddress) => effects |= Effects::ThisContractAddress,
35 _ => {}
36 }
37
38 if effects == Effects::all() {
40 break;
41 }
42 }
43 effects
44}
45
46pub fn bytes_contains_any(bytes: &[u8], effects: Effects) -> bool {
51 let krng_byte: u8 = Op::StateRead(StateRead::KeyRange).to_opcode().into();
52 let krng_extern_byte: u8 = Op::StateRead(StateRead::KeyRangeExtern).to_opcode().into();
53 let post_krng_byte: u8 = Op::StateRead(StateRead::PostKeyRange).to_opcode().into();
54 let post_krng_extern_byte: u8 = Op::StateRead(StateRead::PostKeyRangeExtern)
55 .to_opcode()
56 .into();
57 let this_address_byte: u8 = Op::Access(Access::ThisAddress).to_opcode().into();
58 let this_contract_address_byte: u8 = Op::Access(Access::ThisContractAddress).to_opcode().into();
59
60 let push: u8 = Op::Stack(Stack::Push(0)).to_opcode().into();
61
62 let mut iter = bytes.iter();
63 while let Some(byte) = iter.next() {
64 match byte {
65 b if *b == krng_byte && effects.contains(Effects::KeyRange) => return true,
66 b if *b == krng_extern_byte && effects.contains(Effects::KeyRangeExtern) => {
67 return true
68 }
69 b if *b == post_krng_byte && effects.contains(Effects::PostKeyRange) => return true,
70 b if *b == post_krng_extern_byte && effects.contains(Effects::PostKeyRangeExtern) => {
71 return true
72 }
73 b if *b == this_address_byte && effects.contains(Effects::ThisAddress) => return true,
74 b if *b == this_contract_address_byte
75 && effects.contains(Effects::ThisContractAddress) =>
76 {
77 return true
78 }
79 b if *b == push => {
80 iter.by_ref().take(8).for_each(|_| ());
82 }
83 _ => {}
84 }
85 }
86 false
87}
88
89#[cfg(test)]
90mod test {
91 use essential_types::convert::word_from_bytes;
92
93 use crate::{effects::bytes_contains_any, ToOpcode};
94
95 use super::{analyze, Access, Effects, Op, StateRead};
96
97 #[test]
98 fn none() {
99 let ops = &[];
100 assert_eq!(analyze(ops), Effects::empty());
101 }
102
103 #[test]
104 fn key_range() {
105 let ops = &[Op::StateRead(StateRead::KeyRange)];
106 let effects = analyze(ops);
107 assert!(effects.contains(Effects::KeyRange));
108 }
109
110 #[test]
111 fn key_range_extern() {
112 let ops = &[Op::StateRead(StateRead::KeyRangeExtern)];
113 let effects = analyze(ops);
114 assert!(effects.contains(Effects::KeyRangeExtern));
115 }
116
117 #[test]
118 fn this_address() {
119 let ops = &[Op::Access(Access::ThisAddress)];
120 let effects = analyze(ops);
121 assert!(effects.contains(Effects::ThisAddress));
122 }
123
124 #[test]
125 fn this_contract_address() {
126 let ops = &[Op::Access(Access::ThisContractAddress)];
127 let effects = analyze(ops);
128 assert!(effects.contains(Effects::ThisContractAddress));
129 }
130
131 #[test]
132 fn all_effects() {
133 let ops = &[
134 Op::StateRead(StateRead::KeyRange),
135 Op::StateRead(StateRead::KeyRangeExtern),
136 Op::Access(Access::ThisAddress),
137 Op::Access(Access::ThisContractAddress),
138 ];
139 let effects = analyze(ops);
140 assert!(effects.contains(Effects::KeyRange));
141 assert!(effects.contains(Effects::KeyRangeExtern));
142 assert!(effects.contains(Effects::ThisAddress));
143 assert!(effects.contains(Effects::ThisContractAddress));
144 }
145
146 #[test]
147 fn test_bytes_contains_any() {
148 use crate::short::*;
149 let to_bytes = |ops: &[Op]| crate::to_bytes(ops.iter().copied()).collect::<Vec<u8>>();
150
151 let effects = Effects::all();
153 assert!(!bytes_contains_any(&to_bytes(&[POP, POP, POP]), effects));
154
155 let effects = Effects::KeyRange | Effects::KeyRangeExtern;
157 assert!(!bytes_contains_any(&to_bytes(&[PKRNG, PKREX]), effects));
158
159 let key: u8 = PKRNG.to_opcode().into();
161 let bytes = [key, 0, 0, 0, 0, 0, 0, 0];
162 let word = word_from_bytes(bytes);
163 let effects = Effects::PostKeyRange;
164 assert!(!bytes_contains_any(&to_bytes(&[PUSH(word)]), effects));
165
166 let effects = Effects::KeyRange;
168 assert!(bytes_contains_any(&to_bytes(&[PUSH(word), KRNG]), effects));
169
170 let effects = Effects::KeyRange;
172 assert!(bytes_contains_any(&to_bytes(&[KRNG]), effects));
173
174 let effects = Effects::KeyRangeExtern;
176 assert!(bytes_contains_any(&to_bytes(&[KREX]), effects));
177
178 let effects = Effects::PostKeyRange;
180 assert!(bytes_contains_any(&to_bytes(&[PKRNG]), effects));
181
182 let effects = Effects::PostKeyRangeExtern;
184 assert!(bytes_contains_any(&to_bytes(&[PKREX]), effects));
185
186 let effects = Effects::ThisAddress;
188 assert!(bytes_contains_any(&to_bytes(&[THIS]), effects));
189
190 let effects = Effects::ThisContractAddress;
192 assert!(bytes_contains_any(&to_bytes(&[THISC]), effects));
193
194 let effects = Effects::empty();
196 assert!(!bytes_contains_any(&[], effects));
197
198 let effects = Effects::KeyRange;
199 assert!(!bytes_contains_any(&[], effects));
200 }
201}