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