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 : #ifdef _MSC_VER
6 : // Disable warning C4996: 'getenv': This function or variable may be unsafe.
7 : #define _CRT_SECURE_NO_WARNINGS
8 : #endif
9 :
10 : #include "precompiles_cache.hpp"
11 : #include <nlohmann/json.hpp>
12 : #include <fstream>
13 : #include <iostream>
14 :
15 : namespace evmone::state
16 : {
17 UBC 0 : std::optional<evmc::Result> Cache::find(PrecompileId id, bytes_view input, int64_t gas_left) const
18 : {
19 0 : if (const auto& cache = m_cache.at(stdx::to_underlying(id)); !cache.empty())
20 : {
21 0 : const auto input_hash = keccak256(input);
22 0 : if (const auto it = cache.find(input_hash); it != cache.end())
23 : {
24 0 : if (const auto& o = it->second; !o.has_value())
25 0 : return evmc::Result{EVMC_PRECOMPILE_FAILURE};
26 : else
27 0 : return evmc::Result{EVMC_SUCCESS, gas_left, 0, o->data(), o->size()};
28 : }
29 : }
30 0 : return {};
31 : }
32 :
33 0 : void Cache::insert(PrecompileId id, bytes_view input, const evmc::Result& result)
34 : {
35 0 : if (id == PrecompileId::identity) // Do not cache "identity".
36 0 : return;
37 0 : const auto input_hash = keccak256(input);
38 0 : std::optional<bytes> cached_output;
39 0 : if (result.status_code == EVMC_SUCCESS)
40 0 : cached_output = bytes{result.output_data, result.output_size};
41 0 : m_cache.at(stdx::to_underlying(id)).insert({input_hash, std::move(cached_output)});
42 0 : }
43 :
44 0 : Cache::Cache() noexcept
45 : {
46 0 : const auto stub_file = std::getenv("EVMONE_PRECOMPILES_STUB");
47 0 : if (stub_file == nullptr)
48 0 : return;
49 :
50 : try
51 : {
52 0 : const auto j = nlohmann::json::parse(std::ifstream{stub_file});
53 0 : for (size_t id = 0; id < j.size(); ++id)
54 : {
55 0 : auto& cache = m_cache.at(id);
56 0 : for (const auto& [h_str, j_input] : j[id].items())
57 : {
58 0 : auto& e = cache[evmc::from_hex<hash256>(h_str).value()];
59 0 : if (!j_input.is_null())
60 0 : e = evmc::from_hex(j_input.get<std::string>());
61 0 : }
62 : }
63 0 : }
64 0 : catch (...)
65 : {
66 0 : std::cerr << "evmone: Loading precompiles stub from '" << stub_file << "' has failed!\n";
67 0 : }
68 : }
69 :
70 0 : Cache::~Cache() noexcept
71 : {
72 0 : const auto dump_file = std::getenv("EVMONE_PRECOMPILES_DUMP");
73 0 : if (dump_file == nullptr)
74 0 : return;
75 :
76 : try
77 : {
78 0 : nlohmann::json j;
79 0 : for (size_t id = 0; id < std::size(m_cache); ++id)
80 : {
81 0 : auto& q = j[id];
82 0 : for (const auto& [h, o] : m_cache[id])
83 : {
84 0 : auto& v = q[evmc::hex(h)];
85 0 : if (o)
86 0 : v = evmc::hex(*o);
87 : }
88 : }
89 0 : std::ofstream{dump_file} << std::setw(2) << j << '\n';
90 0 : }
91 0 : catch (...)
92 : {
93 0 : std::cerr << "evmone: Dumping precompiles to '" << dump_file << "' has failed!\n";
94 0 : }
95 0 : }
96 : } // namespace evmone::state
|