1 // EVMC: Ethereum Client-VM Connector API.
2 // Copyright 2019 The EVMC Authors.
3 // Licensed under the Apache License, Version 2.0.
4 #pragma once
5 
6 #include <evmc/evmc.hpp>
7 #include <algorithm>
8 #include <string>
9 #include <unordered_map>
10 #include <vector>
11 
12 namespace evmc
13 {
14 /// The string of bytes.
15 using bytes = std::basic_string<uint8_t>;
16 
17 /// Extended value (by dirty flag) for account storage.
18 struct storage_value
19 {
20     /// The storage value.
21     bytes32 value;
22 
23     /// True means this value has been modified already by the current transaction.
24     bool dirty{false};
25 
26     /// Default constructor.
27     storage_value() noexcept = default;
28 
29     /// Constructor.
storage_valueevmc::storage_value30     storage_value(const bytes32& _value, bool _dirty = false) noexcept  // NOLINT
31       : value{_value}, dirty{_dirty}
32     {}
33 };
34 
35 /// Mocked account.
36 struct MockedAccount
37 {
38     /// The account nonce.
39     int nonce = 0;
40 
41     /// The account code.
42     bytes code;
43 
44     /// The code hash. Can be a value not related to the actual code.
45     bytes32 codehash;
46 
47     /// The account balance.
48     uint256be balance;
49 
50     /// The account storage map.
51     std::unordered_map<bytes32, storage_value> storage;
52 
53     /// Helper method for setting balance by numeric type.
set_balanceevmc::MockedAccount54     void set_balance(uint64_t x) noexcept
55     {
56         balance = uint256be{};
57         for (std::size_t i = 0; i < sizeof(x); ++i)
58             balance.bytes[sizeof(balance) - 1 - i] = static_cast<uint8_t>(x >> (8 * i));
59     }
60 };
61 
62 /// Mocked EVMC Host implementation.
63 class MockedHost : public Host
64 {
65 public:
66     /// LOG record.
67     struct log_record
68     {
69         /// The address of the account which created the log.
70         address creator;
71 
72         /// The data attached to the log.
73         bytes data;
74 
75         /// The log topics.
76         std::vector<bytes32> topics;
77 
78         /// Equal operator.
operator ==evmc::MockedHost::log_record79         bool operator==(const log_record& other) const noexcept
80         {
81             return creator == other.creator && data == other.data && topics == other.topics;
82         }
83     };
84 
85     /// SELFDESTRUCT record.
86     struct selfdestuct_record
87     {
88         /// The address of the account which has self-destructed.
89         address selfdestructed;
90 
91         /// The address of the beneficiary account.
92         address beneficiary;
93 
94         /// Equal operator.
operator ==evmc::MockedHost::selfdestuct_record95         bool operator==(const selfdestuct_record& other) const noexcept
96         {
97             return selfdestructed == other.selfdestructed && beneficiary == other.beneficiary;
98         }
99     };
100 
101     /// The set of all accounts in the Host, organized by their addresses.
102     std::unordered_map<address, MockedAccount> accounts;
103 
104     /// The EVMC transaction context to be returned by get_tx_context().
105     evmc_tx_context tx_context = {};
106 
107     /// The block header hash value to be returned by get_block_hash().
108     bytes32 block_hash = {};
109 
110     /// The call result to be returned by the call() method.
111     evmc_result call_result = {};
112 
113     /// The record of all block numbers for which get_block_hash() was called.
114     mutable std::vector<int64_t> recorded_blockhashes;
115 
116     /// The record of all account accesses.
117     mutable std::vector<address> recorded_account_accesses;
118 
119     /// The maximum number of entries in recorded_account_accesses record.
120     /// This is arbitrary value useful in fuzzing when we don't want the record to explode.
121     static constexpr auto max_recorded_account_accesses = 200;
122 
123     /// The record of all call messages requested in the call() method.
124     std::vector<evmc_message> recorded_calls;
125 
126     /// The maximum number of entries in recorded_calls record.
127     /// This is arbitrary value useful in fuzzing when we don't want the record to explode.
128     static constexpr auto max_recorded_calls = 100;
129 
130     /// The record of all LOGs passed to the emit_log() method.
131     std::vector<log_record> recorded_logs;
132 
133     /// The record of all SELFDESTRUCTs from the selfdestruct() method.
134     std::vector<selfdestuct_record> recorded_selfdestructs;
135 
136 protected:
137     /// The copy of call inputs for the recorded_calls record.
138     std::vector<bytes> m_recorded_calls_inputs;
139 
140     /// Record an account access.
141     /// @param addr  The address of the accessed account.
record_account_access(const address & addr) const142     void record_account_access(const address& addr) const
143     {
144         if (recorded_account_accesses.empty())
145             recorded_account_accesses.reserve(max_recorded_account_accesses);
146 
147         if (recorded_account_accesses.size() < max_recorded_account_accesses)
148             recorded_account_accesses.emplace_back(addr);
149     }
150 
151     /// Returns true if an account exists (EVMC Host method).
account_exists(const address & addr) const152     bool account_exists(const address& addr) const noexcept override
153     {
154         record_account_access(addr);
155         return accounts.count(addr) != 0;
156     }
157 
158     /// Get the account's storage value at the given key (EVMC Host method).
get_storage(const address & addr,const bytes32 & key) const159     bytes32 get_storage(const address& addr, const bytes32& key) const noexcept override
160     {
161         record_account_access(addr);
162 
163         const auto account_iter = accounts.find(addr);
164         if (account_iter == accounts.end())
165             return {};
166 
167         const auto storage_iter = account_iter->second.storage.find(key);
168         if (storage_iter != account_iter->second.storage.end())
169             return storage_iter->second.value;
170         return {};
171     }
172 
173     /// Set the account's storage value (EVMC Host method).
set_storage(const address & addr,const bytes32 & key,const bytes32 & value)174     evmc_storage_status set_storage(const address& addr,
175                                     const bytes32& key,
176                                     const bytes32& value) noexcept override
177     {
178         record_account_access(addr);
179         const auto it = accounts.find(addr);
180         if (it == accounts.end())
181             return EVMC_STORAGE_UNCHANGED;
182 
183         auto& old = it->second.storage[key];
184 
185         // Follow https://eips.ethereum.org/EIPS/eip-1283 specification.
186         // WARNING! This is not complete implementation as refund is not handled here.
187 
188         if (old.value == value)
189             return EVMC_STORAGE_UNCHANGED;
190 
191         evmc_storage_status status{};
192         if (!old.dirty)
193         {
194             old.dirty = true;
195             if (!old.value)
196                 status = EVMC_STORAGE_ADDED;
197             else if (value)
198                 status = EVMC_STORAGE_MODIFIED;
199             else
200                 status = EVMC_STORAGE_DELETED;
201         }
202         else
203             status = EVMC_STORAGE_MODIFIED_AGAIN;
204 
205         old.value = value;
206         return status;
207     }
208 
209     /// Get the account's balance (EVMC Host method).
get_balance(const address & addr) const210     uint256be get_balance(const address& addr) const noexcept override
211     {
212         record_account_access(addr);
213         const auto it = accounts.find(addr);
214         if (it == accounts.end())
215             return {};
216 
217         return it->second.balance;
218     }
219 
220     /// Get the account's code size (EVMC host method).
get_code_size(const address & addr) const221     size_t get_code_size(const address& addr) const noexcept override
222     {
223         record_account_access(addr);
224         const auto it = accounts.find(addr);
225         if (it == accounts.end())
226             return 0;
227         return it->second.code.size();
228     }
229 
230     /// Get the account's code hash (EVMC host method).
get_code_hash(const address & addr) const231     bytes32 get_code_hash(const address& addr) const noexcept override
232     {
233         record_account_access(addr);
234         const auto it = accounts.find(addr);
235         if (it == accounts.end())
236             return {};
237         return it->second.codehash;
238     }
239 
240     /// Copy the account's code to the given buffer (EVMC host method).
copy_code(const address & addr,size_t code_offset,uint8_t * buffer_data,size_t buffer_size) const241     size_t copy_code(const address& addr,
242                      size_t code_offset,
243                      uint8_t* buffer_data,
244                      size_t buffer_size) const noexcept override
245     {
246         record_account_access(addr);
247         const auto it = accounts.find(addr);
248         if (it == accounts.end())
249             return 0;
250 
251         const auto& code = it->second.code;
252 
253         if (code_offset >= code.size())
254             return 0;
255 
256         const auto n = std::min(buffer_size, code.size() - code_offset);
257 
258         if (n > 0)
259             std::copy_n(&code[code_offset], n, buffer_data);
260         return n;
261     }
262 
263     /// Selfdestruct the account (EVMC host method).
selfdestruct(const address & addr,const address & beneficiary)264     void selfdestruct(const address& addr, const address& beneficiary) noexcept override
265     {
266         record_account_access(addr);
267         recorded_selfdestructs.push_back({addr, beneficiary});
268     }
269 
270     /// Call/create other contract (EVMC host method).
call(const evmc_message & msg)271     result call(const evmc_message& msg) noexcept override
272     {
273         record_account_access(msg.destination);
274 
275         if (recorded_calls.empty())
276         {
277             recorded_calls.reserve(max_recorded_calls);
278             m_recorded_calls_inputs.reserve(max_recorded_calls);  // Iterators will not invalidate.
279         }
280 
281         if (recorded_calls.size() < max_recorded_calls)
282         {
283             recorded_calls.emplace_back(msg);
284             auto& call_msg = recorded_calls.back();
285             if (call_msg.input_size > 0)
286             {
287                 m_recorded_calls_inputs.emplace_back(call_msg.input_data, call_msg.input_size);
288                 const auto& input_copy = m_recorded_calls_inputs.back();
289                 call_msg.input_data = input_copy.data();
290             }
291         }
292         return result{call_result};
293     }
294 
295     /// Get transaction context (EVMC host method).
get_tx_context() const296     evmc_tx_context get_tx_context() const noexcept override { return tx_context; }
297 
298     /// Get the block header hash (EVMC host method).
get_block_hash(int64_t block_number) const299     bytes32 get_block_hash(int64_t block_number) const noexcept override
300     {
301         recorded_blockhashes.emplace_back(block_number);
302         return block_hash;
303     }
304 
305     /// Emit LOG (EVMC host method).
emit_log(const address & addr,const uint8_t * data,size_t data_size,const bytes32 topics[],size_t topics_count)306     void emit_log(const address& addr,
307                   const uint8_t* data,
308                   size_t data_size,
309                   const bytes32 topics[],
310                   size_t topics_count) noexcept override
311     {
312         recorded_logs.push_back({addr, {data, data_size}, {topics, topics + topics_count}});
313     }
314 };
315 }  // namespace evmc
316