TLA Line data Source code
1 : // evmone: Fast Ethereum Virtual Machine implementation
2 : // Copyright 2022 The evmone Authors.
3 : // SPDX-License-Identifier: Apache-2.0
4 :
5 : #include "precompiles.hpp"
6 : #include "precompiles_cache.hpp"
7 : #include "precompiles_internal.hpp"
8 : #include <evmone_precompiles/bn254.hpp>
9 : #include <evmone_precompiles/secp256k1.hpp>
10 : #include <intx/intx.hpp>
11 : #include <bit>
12 : #include <cassert>
13 : #include <iostream>
14 : #include <limits>
15 : #include <unordered_map>
16 :
17 : #ifdef EVMONE_PRECOMPILES_SILKPRE
18 : #include "precompiles_silkpre.hpp"
19 : #endif
20 :
21 : namespace evmone::state
22 : {
23 : using namespace evmc::literals;
24 :
25 : namespace
26 : {
27 : constexpr auto GasCostMax = std::numeric_limits<int64_t>::max();
28 :
29 UBC 0 : inline constexpr int64_t num_words(size_t size_in_bytes) noexcept
30 : {
31 0 : return static_cast<int64_t>((size_in_bytes + 31) / 32);
32 : }
33 :
34 : template <int BaseCost, int WordCost>
35 0 : inline constexpr int64_t cost_per_input_word(size_t input_size) noexcept
36 : {
37 0 : return BaseCost + WordCost * num_words(input_size);
38 : }
39 : } // namespace
40 :
41 0 : PrecompileAnalysis ecrecover_analyze(bytes_view /*input*/, evmc_revision /*rev*/) noexcept
42 : {
43 0 : return {3000, 32};
44 : }
45 :
46 0 : PrecompileAnalysis sha256_analyze(bytes_view input, evmc_revision /*rev*/) noexcept
47 : {
48 0 : return {cost_per_input_word<60, 12>(input.size()), 32};
49 : }
50 :
51 0 : PrecompileAnalysis ripemd160_analyze(bytes_view input, evmc_revision /*rev*/) noexcept
52 : {
53 0 : return {cost_per_input_word<600, 120>(input.size()), 32};
54 : }
55 :
56 0 : PrecompileAnalysis identity_analyze(bytes_view input, evmc_revision /*rev*/) noexcept
57 : {
58 0 : return {cost_per_input_word<15, 3>(input.size()), input.size()};
59 : }
60 :
61 0 : PrecompileAnalysis ecadd_analyze(bytes_view /*input*/, evmc_revision rev) noexcept
62 : {
63 0 : return {rev >= EVMC_ISTANBUL ? 150 : 500, 64};
64 : }
65 :
66 0 : PrecompileAnalysis ecmul_analyze(bytes_view /*input*/, evmc_revision rev) noexcept
67 : {
68 0 : return {rev >= EVMC_ISTANBUL ? 6000 : 40000, 64};
69 : }
70 :
71 0 : PrecompileAnalysis ecpairing_analyze(bytes_view input, evmc_revision rev) noexcept
72 : {
73 0 : const auto base_cost = (rev >= EVMC_ISTANBUL) ? 45000 : 100000;
74 0 : const auto element_cost = (rev >= EVMC_ISTANBUL) ? 34000 : 80000;
75 0 : const auto num_elements = static_cast<int64_t>(input.size() / 192);
76 0 : return {base_cost + num_elements * element_cost, 32};
77 : }
78 :
79 0 : PrecompileAnalysis blake2bf_analyze(bytes_view input, evmc_revision) noexcept
80 : {
81 0 : return {input.size() == 213 ? intx::be::unsafe::load<uint32_t>(input.data()) : GasCostMax, 64};
82 : }
83 :
84 0 : PrecompileAnalysis expmod_analyze(bytes_view input, evmc_revision rev) noexcept
85 : {
86 : using namespace intx;
87 :
88 : static constexpr size_t input_header_required_size = 3 * sizeof(uint256);
89 0 : const int64_t min_gas = (rev >= EVMC_BERLIN) ? 200 : 0;
90 :
91 0 : uint8_t input_header[input_header_required_size]{};
92 0 : std::copy_n(input.data(), std::min(input.size(), input_header_required_size), input_header);
93 :
94 0 : const auto base_len = be::unsafe::load<uint256>(&input_header[0]);
95 0 : const auto exp_len = be::unsafe::load<uint256>(&input_header[32]);
96 0 : const auto mod_len = be::unsafe::load<uint256>(&input_header[64]);
97 :
98 0 : if (base_len == 0 && mod_len == 0)
99 0 : return {min_gas, 0};
100 :
101 : static constexpr auto len_limit = std::numeric_limits<size_t>::max();
102 0 : if (base_len > len_limit || exp_len > len_limit || mod_len > len_limit)
103 0 : return {GasCostMax, 0};
104 :
105 0 : auto adjusted_len = [input](size_t offset, size_t len) {
106 0 : const auto head_len = std::min(len, size_t{32});
107 : const auto head_explicit_len =
108 0 : std::max(std::min(offset + head_len, input.size()), offset) - offset;
109 0 : const bytes_view head_explicit_bytes(&input[offset], head_explicit_len);
110 0 : const auto top_byte_index = head_explicit_bytes.find_first_not_of(uint8_t{0});
111 : const size_t exp_bit_width =
112 0 : (top_byte_index != bytes_view::npos) ?
113 0 : (head_len - top_byte_index - 1) * 8 +
114 0 : static_cast<size_t>(std::bit_width(head_explicit_bytes[top_byte_index])) :
115 0 : 0;
116 :
117 0 : return std::max(
118 0 : 8 * (std::max(len, size_t{32}) - 32) + (std::max(exp_bit_width, size_t{1}) - 1),
119 0 : size_t{1});
120 0 : };
121 :
122 0 : static constexpr auto mult_complexity_eip2565 = [](const uint256& x) noexcept {
123 0 : const auto w = (x + 7) >> 3;
124 0 : return w * w;
125 : };
126 0 : static constexpr auto mult_complexity_eip198 = [](const uint256& x) noexcept {
127 0 : const auto x2 = x * x;
128 0 : return (x <= 64) ? x2 :
129 0 : (x <= 1024) ? (x2 >> 2) + 96 * x - 3072 :
130 0 : (x2 >> 4) + 480 * x - 199680;
131 : };
132 :
133 0 : const auto max_len = std::max(mod_len, base_len);
134 0 : const auto adjusted_exp_len = adjusted_len(
135 0 : sizeof(input_header) + static_cast<size_t>(base_len), static_cast<size_t>(exp_len));
136 0 : const auto gas = (rev >= EVMC_BERLIN) ?
137 0 : mult_complexity_eip2565(max_len) * adjusted_exp_len / 3 :
138 0 : mult_complexity_eip198(max_len) * adjusted_exp_len / 20;
139 0 : return {std::max(min_gas, static_cast<int64_t>(std::min(gas, intx::uint256{GasCostMax}))),
140 0 : static_cast<size_t>(mod_len)};
141 : }
142 :
143 0 : PrecompileAnalysis point_evaluation_analyze(bytes_view, evmc_revision) noexcept
144 : {
145 : static constexpr auto POINT_EVALUATION_PRECOMPILE_GAS = 50000;
146 0 : return {POINT_EVALUATION_PRECOMPILE_GAS, 64};
147 : }
148 :
149 0 : ExecutionResult ecrecover_execute(const uint8_t* input, size_t input_size, uint8_t* output,
150 : [[maybe_unused]] size_t output_size) noexcept
151 : {
152 0 : assert(output_size >= 32);
153 :
154 0 : uint8_t input_buffer[128]{};
155 0 : if (input_size != 0)
156 0 : std::memcpy(input_buffer, input, std::min(input_size, std::size(input_buffer)));
157 :
158 0 : ethash::hash256 h{};
159 0 : std::memcpy(h.bytes, input_buffer, sizeof(h));
160 :
161 0 : const auto v = intx::be::unsafe::load<intx::uint256>(input_buffer + 32);
162 0 : if (v != 27 && v != 28)
163 0 : return {EVMC_SUCCESS, 0};
164 0 : const bool parity = v == 28;
165 :
166 0 : const auto r = intx::be::unsafe::load<intx::uint256>(input_buffer + 64);
167 0 : const auto s = intx::be::unsafe::load<intx::uint256>(input_buffer + 96);
168 :
169 0 : const auto res = evmmax::secp256k1::ecrecover(h, r, s, parity);
170 0 : if (res)
171 : {
172 0 : std::memset(output, 0, 12);
173 0 : std::memcpy(output + 12, res->bytes, 20);
174 0 : return {EVMC_SUCCESS, 32};
175 : }
176 : else
177 0 : return {EVMC_SUCCESS, 0};
178 : }
179 :
180 0 : ExecutionResult ecadd_execute(const uint8_t* input, size_t input_size, uint8_t* output,
181 : [[maybe_unused]] size_t output_size) noexcept
182 : {
183 0 : assert(output_size >= 64);
184 :
185 0 : uint8_t input_buffer[128]{};
186 0 : if (input_size != 0)
187 0 : std::memcpy(input_buffer, input, std::min(input_size, std::size(input_buffer)));
188 :
189 0 : ethash::hash256 h{};
190 0 : std::memcpy(h.bytes, input_buffer, sizeof(h));
191 :
192 0 : const evmmax::bn254::Point p = {intx::be::unsafe::load<intx::uint256>(input_buffer),
193 0 : intx::be::unsafe::load<intx::uint256>(input_buffer + 32)};
194 0 : const evmmax::bn254::Point q = {intx::be::unsafe::load<intx::uint256>(input_buffer + 64),
195 0 : intx::be::unsafe::load<intx::uint256>(input_buffer + 96)};
196 :
197 0 : if (evmmax::bn254::validate(p) && evmmax::bn254::validate(q))
198 : {
199 0 : const auto res = evmmax::bn254::add(p, q);
200 0 : intx::be::unsafe::store(output, res.x);
201 0 : intx::be::unsafe::store(output + 32, res.y);
202 0 : return {EVMC_SUCCESS, 64};
203 : }
204 : else
205 0 : return {EVMC_PRECOMPILE_FAILURE, 0};
206 : }
207 :
208 0 : ExecutionResult ecmul_execute(const uint8_t* input, size_t input_size, uint8_t* output,
209 : [[maybe_unused]] size_t output_size) noexcept
210 : {
211 0 : assert(output_size >= 64);
212 :
213 0 : uint8_t input_buffer[96]{};
214 0 : if (input_size != 0)
215 0 : std::memcpy(input_buffer, input, std::min(input_size, std::size(input_buffer)));
216 :
217 0 : ethash::hash256 h{};
218 0 : std::memcpy(h.bytes, input_buffer, sizeof(h));
219 :
220 0 : const evmmax::bn254::Point p = {intx::be::unsafe::load<intx::uint256>(input_buffer),
221 0 : intx::be::unsafe::load<intx::uint256>(input_buffer + 32)};
222 0 : const auto c = intx::be::unsafe::load<intx::uint256>(input_buffer + 64);
223 :
224 0 : if (evmmax::bn254::validate(p))
225 : {
226 0 : const auto res = evmmax::bn254::mul(p, c);
227 0 : intx::be::unsafe::store(output, res.x);
228 0 : intx::be::unsafe::store(output + 32, res.y);
229 0 : return {EVMC_SUCCESS, 64};
230 : }
231 : else
232 0 : return {EVMC_PRECOMPILE_FAILURE, 0};
233 : }
234 :
235 0 : ExecutionResult identity_execute(const uint8_t* input, size_t input_size, uint8_t* output,
236 : [[maybe_unused]] size_t output_size) noexcept
237 : {
238 0 : assert(output_size >= input_size);
239 0 : std::copy_n(input, input_size, output);
240 0 : return {EVMC_SUCCESS, input_size};
241 : }
242 :
243 : namespace
244 : {
245 : struct PrecompileTraits
246 : {
247 : decltype(identity_analyze)* analyze = nullptr;
248 : decltype(identity_execute)* execute = nullptr;
249 : };
250 :
251 : template <PrecompileId Id>
252 0 : ExecutionResult dummy_execute(const uint8_t*, size_t, uint8_t*, size_t) noexcept
253 : {
254 0 : std::cerr << "Precompile " << static_cast<int>(Id) << " not implemented!\n";
255 0 : return ExecutionResult{EVMC_INTERNAL_ERROR, 0};
256 : }
257 :
258 : inline constexpr auto traits = []() noexcept {
259 : std::array<PrecompileTraits, NumPrecompiles> tbl{{
260 : {}, // undefined for 0
261 : {ecrecover_analyze, ecrecover_execute},
262 : {sha256_analyze, dummy_execute<PrecompileId::sha256>},
263 : {ripemd160_analyze, dummy_execute<PrecompileId::ripemd160>},
264 : {identity_analyze, identity_execute},
265 : {expmod_analyze, dummy_execute<PrecompileId::expmod>},
266 : {ecadd_analyze, ecadd_execute},
267 : {ecmul_analyze, ecmul_execute},
268 : {ecpairing_analyze, dummy_execute<PrecompileId::ecpairing>},
269 : {blake2bf_analyze, dummy_execute<PrecompileId::blake2bf>},
270 : {point_evaluation_analyze, dummy_execute<PrecompileId::point_evaluation>},
271 : }};
272 : #ifdef EVMONE_PRECOMPILES_SILKPRE
273 : // tbl[static_cast<size_t>(PrecompileId::ecrecover)].execute = silkpre_ecrecover_execute;
274 : tbl[static_cast<size_t>(PrecompileId::sha256)].execute = silkpre_sha256_execute;
275 : tbl[static_cast<size_t>(PrecompileId::ripemd160)].execute = silkpre_ripemd160_execute;
276 : tbl[static_cast<size_t>(PrecompileId::expmod)].execute = silkpre_expmod_execute;
277 : // tbl[static_cast<size_t>(PrecompileId::ecadd)].execute = silkpre_ecadd_execute;
278 : // tbl[static_cast<size_t>(PrecompileId::ecmul)].execute = silkpre_ecmul_execute;
279 : tbl[static_cast<size_t>(PrecompileId::ecpairing)].execute = silkpre_ecpairing_execute;
280 : tbl[static_cast<size_t>(PrecompileId::blake2bf)].execute = silkpre_blake2bf_execute;
281 : #endif
282 : return tbl;
283 : }();
284 : } // namespace
285 :
286 CBC 239 : bool is_precompile(evmc_revision rev, const evmc::address& addr) noexcept
287 : {
288 : // Define compile-time constant,
289 : // TODO(clang18): workaround for Clang Analyzer bug, fixed in clang 18.
290 : // https://github.com/llvm/llvm-project/issues/59493.
291 : static constexpr evmc::address address_boundary{stdx::to_underlying(PrecompileId::latest)};
292 :
293 239 : if (evmc::is_zero(addr) || addr > address_boundary)
294 239 : return false;
295 :
296 UBC 0 : const auto id = addr.bytes[19];
297 0 : if (rev < EVMC_BYZANTIUM && id >= stdx::to_underlying(PrecompileId::since_byzantium))
298 0 : return false;
299 :
300 0 : if (rev < EVMC_ISTANBUL && id >= stdx::to_underlying(PrecompileId::since_istanbul))
301 0 : return false;
302 :
303 0 : if (rev < EVMC_CANCUN && id >= stdx::to_underlying(PrecompileId::since_cancun))
304 0 : return false;
305 :
306 0 : return true;
307 : }
308 :
309 0 : evmc::Result call_precompile(evmc_revision rev, const evmc_message& msg) noexcept
310 : {
311 0 : assert(msg.gas >= 0);
312 :
313 0 : const auto id = msg.code_address.bytes[19];
314 0 : const auto [analyze, execute] = traits[id];
315 :
316 0 : const bytes_view input{msg.input_data, msg.input_size};
317 0 : const auto [gas_cost, max_output_size] = analyze(input, rev);
318 0 : const auto gas_left = msg.gas - gas_cost;
319 0 : if (gas_left < 0)
320 0 : return evmc::Result{EVMC_OUT_OF_GAS};
321 :
322 0 : static Cache cache;
323 0 : if (auto r = cache.find(static_cast<PrecompileId>(id), input, gas_left); r.has_value())
324 0 : return std::move(*r);
325 :
326 : // Buffer for the precompile's output.
327 : // Big enough to handle all "expmod" tests, but in case does not match the size requirement
328 : // from analysis, the result will likely be incorrect.
329 : // TODO: Replace with std::pmr::monotonic_buffer_resource?
330 : uint8_t output_buf[4096];
331 0 : assert(std::size(output_buf) >= max_output_size);
332 :
333 : const auto [status_code, output_size] =
334 0 : execute(msg.input_data, msg.input_size, output_buf, std::size(output_buf));
335 :
336 : evmc::Result result{
337 0 : status_code, status_code == EVMC_SUCCESS ? gas_left : 0, 0, output_buf, output_size};
338 :
339 0 : cache.insert(static_cast<PrecompileId>(id), input, result);
340 :
341 0 : return result;
342 0 : }
343 : } // namespace evmone::state
|