foundry_evm_symbolic/runtime/
precompiles.rs1use super::*;
2
3pub(crate) fn is_known_cheatcode(address: Address) -> bool {
5 address == CHEATCODE_ADDRESS || address == SYMBOLIC_VM_COMPAT_ADDRESS
6}
7
8pub(crate) fn is_console(address: Address) -> bool {
10 address == HARDHAT_CONSOLE_ADDRESS
11}
12
13pub(crate) fn precompile_number(address: Address) -> Option<u8> {
15 let bytes = address.as_slice();
16 if bytes[..PRECOMPILE_ADDRESS_LEADING_ZEROS].iter().any(|byte| *byte != 0) {
17 return None;
18 }
19 match bytes[PRECOMPILE_ADDRESS_LEADING_ZEROS] {
20 1..=10 => Some(bytes[PRECOMPILE_ADDRESS_LEADING_ZEROS]),
21 _ => None,
22 }
23}
24
25pub(crate) fn precompile_address(number: u8) -> Address {
27 let mut bytes = [0u8; 20];
28 bytes[PRECOMPILE_ADDRESS_LEADING_ZEROS] = number;
29 Address::from(bytes)
30}
31
32pub(crate) fn is_supported_precompile(address: Address) -> bool {
34 precompile_number(address).is_some()
35}
36
37pub(crate) fn execute_precompile(
39 address: Address,
40 input: &[u8],
41) -> Result<Option<SymReturnData>, SymbolicError> {
42 let output = match precompile_number(address) {
43 Some(1) => secp256k1::ec_recover_run(input, u64::MAX),
44 Some(2) => hash::sha256_run(input, u64::MAX),
45 Some(3) => hash::ripemd160_run(input, u64::MAX),
46 Some(4) => identity::identity_run(input, u64::MAX),
47 Some(5) => modexp::berlin_run(input, u64::MAX),
48 Some(6) => bn254::run_add(input, bn254::add::ISTANBUL_ADD_GAS_COST, u64::MAX),
49 Some(7) => bn254::run_mul(input, bn254::mul::ISTANBUL_MUL_GAS_COST, u64::MAX),
50 Some(8) => bn254::run_pair(
51 input,
52 bn254::pair::ISTANBUL_PAIR_PER_POINT,
53 bn254::pair::ISTANBUL_PAIR_BASE,
54 u64::MAX,
55 ),
56 Some(9) => blake2::run(input, u64::MAX),
57 Some(10) => {
58 return Err(SymbolicError::Unsupported("KZG point-evaluation precompile not modeled"));
59 }
60 _ => return Err(SymbolicError::Unsupported("unsupported precompile")),
61 };
62
63 match output {
64 Ok(output) => Ok(Some(SymReturnData::from_concrete_bytes(output.bytes.to_vec()))),
65 Err(_) => Ok(None),
66 }
67}
68
69pub(crate) fn execute_symbolic_precompile(
71 address: Address,
72 input: Vec<SymWord>,
73 input_len: SymWord,
74) -> Result<Option<SymReturnData>, SymbolicError> {
75 if input.iter().all(|byte| matches!(byte, SymWord::Concrete(_)))
76 && let SymWord::Concrete(input_len) = input_len
77 && input_len <= U256::from(input.len())
78 {
79 let input_len = input_len.to::<usize>();
80 let input = concrete_bytes(&input[..input_len], "symbolic precompile input")?;
81 return execute_precompile(address, &input);
82 }
83
84 match precompile_number(address) {
85 Some(1) => {
86 let word = symbolic_hash_word_with_len("ecrecover", input, input_len);
87 let mut bytes = vec![SymWord::zero(); 12];
88 bytes.extend((12..32).map(|idx| byte_word(U256::from(idx), word.clone())));
89 Ok(Some(SymReturnData::from_symbolic_bytes(bytes)))
90 }
91 Some(2) => Ok(Some(SymReturnData::from_symbolic_bytes(word_bytes(
92 symbolic_hash_word_with_len("sha256", input, input_len),
93 )))),
94 Some(3) => {
95 let word = symbolic_hash_word_with_len("ripemd160", input, input_len);
96 let mut bytes = vec![SymWord::zero(); 12];
97 bytes.extend((12..32).map(|idx| byte_word(U256::from(idx), word.clone())));
98 Ok(Some(SymReturnData::from_symbolic_bytes(bytes)))
99 }
100 Some(4) => Ok(Some(SymReturnData::from_symbolic_bytes_with_len(input, input_len))),
101 Some(5) => symbolic_modexp_precompile(input, input_len),
102 Some(6) => {
103 let input_len = input_len.into_usize("symbolic precompile input")?;
104 if input_len > input.len() {
105 return Err(SymbolicError::Unsupported("out-of-bounds symbolic precompile input"));
106 }
107 if input_has_symbolic_bytes(&input, input_len) {
108 return Err(SymbolicError::Unsupported(
109 "symbolic bn254 precompile validity not modeled",
110 ));
111 }
112 Ok(Some(symbolic_fixed_len_precompile_output("bn254_add", input, input_len, 64)))
113 }
114 Some(7) => {
115 let input_len = input_len.into_usize("symbolic precompile input")?;
116 if input_len > input.len() {
117 return Err(SymbolicError::Unsupported("out-of-bounds symbolic precompile input"));
118 }
119 if input_has_symbolic_bytes(&input, input_len) {
120 return Err(SymbolicError::Unsupported(
121 "symbolic bn254 precompile validity not modeled",
122 ));
123 }
124 Ok(Some(symbolic_fixed_len_precompile_output("bn254_mul", input, input_len, 64)))
125 }
126 Some(8) => {
127 let input_len = input_len.into_usize("symbolic precompile input")?;
128 if input_len % 192 != 0 {
129 return Ok(None);
130 }
131 if input_len > input.len() {
132 return Err(SymbolicError::Unsupported("out-of-bounds symbolic precompile input"));
133 }
134 if input_has_symbolic_bytes(&input, input_len) {
135 return Err(SymbolicError::Unsupported(
136 "symbolic bn254 precompile validity not modeled",
137 ));
138 }
139 Ok(Some(symbolic_fixed_len_precompile_output("bn254_pairing", input, input_len, 32)))
140 }
141 Some(9) => {
142 let input_len = input_len.into_usize("symbolic precompile input")?;
143 if input_len != 213 {
144 return Ok(None);
145 }
146 if input_len > input.len() {
147 return Err(SymbolicError::Unsupported("out-of-bounds symbolic precompile input"));
148 }
149 match input.get(212) {
150 Some(SymWord::Concrete(flag)) if flag.is_zero() || *flag == U256::from(1) => {}
151 Some(SymWord::Concrete(_)) => return Ok(None),
152 Some(SymWord::Expr(_)) => {
153 return Err(SymbolicError::Unsupported(
154 "symbolic blake2f precompile final flag not modeled",
155 ));
156 }
157 None => {
158 return Err(SymbolicError::Unsupported(
159 "out-of-bounds symbolic precompile input",
160 ));
161 }
162 }
163 Ok(Some(symbolic_fixed_len_precompile_output("blake2f", input, input_len, 64)))
164 }
165 Some(10) => Err(SymbolicError::Unsupported("KZG point-evaluation precompile not modeled")),
166 _ => {
167 let input_len = input_len.into_usize("symbolic precompile input")?;
168 let input = concrete_bytes(
169 input
170 .get(..input_len)
171 .ok_or(SymbolicError::Unsupported("out-of-bounds symbolic precompile input"))?,
172 "symbolic precompile input",
173 )?;
174 execute_precompile(address, &input)
175 }
176 }
177}
178
179fn input_has_symbolic_bytes(input: &[SymWord], input_len: usize) -> bool {
180 input.iter().take(input_len).any(|byte| matches!(byte, SymWord::Expr(_)))
181}
182
183pub(crate) fn symbolic_modexp_precompile(
185 input: Vec<SymWord>,
186 input_len: SymWord,
187) -> Result<Option<SymReturnData>, SymbolicError> {
188 let input_len = input_len.into_usize("symbolic precompile input")?;
189 if input_len > input.len() {
190 return Err(SymbolicError::Unsupported("out-of-bounds symbolic precompile input"));
191 }
192
193 let modulus_len = concrete_precompile_word_at(&input, 64)?;
194 let modulus_len = u256_to_usize(modulus_len)
195 .ok_or(SymbolicError::Unsupported("symbolic modexp output length"))?;
196 if modulus_len > 4096 {
197 return Err(SymbolicError::Unsupported("symbolic modexp output length"));
198 }
199 Ok(Some(symbolic_fixed_len_precompile_output("modexp", input, input_len, modulus_len)))
200}
201
202pub(crate) fn concrete_precompile_word_at(
204 input: &[SymWord],
205 offset: usize,
206) -> Result<U256, SymbolicError> {
207 let mut bytes = [0u8; 32];
208 for (idx, byte) in bytes.iter_mut().enumerate() {
209 *byte = match input.get(offset + idx) {
210 Some(SymWord::Concrete(byte)) => byte.to::<u8>(),
211 Some(_) => return Err(SymbolicError::Unsupported("symbolic precompile length header")),
212 None => 0,
213 };
214 }
215 Ok(U256::from_be_bytes(bytes))
216}
217
218pub(crate) fn symbolic_fixed_len_precompile_output(
220 algorithm: &'static str,
221 input: Vec<SymWord>,
222 input_len: usize,
223 output_len: usize,
224) -> SymReturnData {
225 let input_len_word = SymWord::Concrete(U256::from(input_len));
226 let mut bytes = Vec::with_capacity(output_len);
227 for chunk in 0..output_len.div_ceil(32) {
228 let mut chunk_input = Vec::with_capacity(input.len() + 1);
229 chunk_input.push(SymWord::Concrete(U256::from(chunk)));
230 chunk_input.extend(input.iter().cloned());
231 bytes.extend(word_bytes(symbolic_hash_word_with_len(
232 algorithm,
233 chunk_input,
234 input_len_word.clone(),
235 )));
236 }
237 bytes.truncate(output_len);
238 SymReturnData::from_symbolic_bytes(bytes)
239}