|
13 | 13 | // You should have received a copy of the GNU General Public License |
14 | 14 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
15 | 15 |
|
| 16 | +#include <category/core/assert.h> |
16 | 17 | #include <category/core/bytes.hpp> |
17 | 18 | #include <category/core/config.hpp> |
| 19 | +#include <category/core/int.hpp> |
18 | 20 | #include <category/core/keccak.hpp> |
19 | 21 | #include <category/core/result.hpp> |
| 22 | +#include <category/execution/ethereum/core/account.hpp> |
20 | 23 | #include <category/execution/ethereum/core/address.hpp> |
21 | 24 | #include <category/execution/ethereum/core/eth_ctypes.h> |
22 | 25 | #include <category/execution/ethereum/core/receipt.hpp> |
|
26 | 29 | #include <category/execution/ethereum/event/exec_event_recorder.hpp> |
27 | 30 | #include <category/execution/ethereum/event/record_txn_events.hpp> |
28 | 31 | #include <category/execution/ethereum/execute_transaction.hpp> |
| 32 | +#include <category/execution/ethereum/state3/account_state.hpp> |
| 33 | +#include <category/execution/ethereum/state3/state.hpp> |
| 34 | +#include <category/execution/ethereum/state3/version_stack.hpp> |
29 | 35 | #include <category/execution/ethereum/trace/call_frame.hpp> |
30 | 36 | #include <category/execution/ethereum/validate_transaction.hpp> |
31 | 37 |
|
32 | 38 | #include <bit> |
| 39 | +#include <cstddef> |
33 | 40 | #include <cstdint> |
| 41 | +#include <memory> |
34 | 42 | #include <optional> |
35 | 43 | #include <span> |
36 | 44 | #include <utility> |
@@ -67,6 +75,216 @@ void init_txn_header_start( |
67 | 75 | static_cast<uint32_t>(txn.authorization_list.size()); |
68 | 76 | } |
69 | 77 |
|
| 78 | +// Tracks information about an accessed account, including (1) the prestate and |
| 79 | +// the (2) the modified state if a write access modified anything, with helper |
| 80 | +// functions to determine what was modified |
| 81 | +struct AccountAccessInfo |
| 82 | +{ |
| 83 | + Address const *address; |
| 84 | + OriginalAccountState const *prestate; // State as it existed in original |
| 85 | + AccountState const *modified_state; // Last state as it existed in current |
| 86 | + |
| 87 | + bool is_read_only_access() const |
| 88 | + { |
| 89 | + return modified_state == nullptr; |
| 90 | + } |
| 91 | + |
| 92 | + std::pair<uint64_t, bool> get_nonce_modification() const |
| 93 | + { |
| 94 | + if (is_read_only_access()) { |
| 95 | + return {0, false}; |
| 96 | + } |
| 97 | + |
| 98 | + std::optional<Account> const &prestate_account = |
| 99 | + get_account_for_trace(*prestate); |
| 100 | + std::optional<Account> const &modified_account = |
| 101 | + get_account_for_trace(*modified_state); |
| 102 | + |
| 103 | + uint64_t const prestate_nonce = |
| 104 | + is_dead(prestate_account) ? 0 : prestate_account->nonce; |
| 105 | + uint64_t const modified_nonce = |
| 106 | + is_dead(modified_account) ? 0 : modified_account->nonce; |
| 107 | + return {modified_nonce, prestate_nonce != modified_nonce}; |
| 108 | + } |
| 109 | + |
| 110 | + std::pair<uint256_t, bool> get_balance_modification() const |
| 111 | + { |
| 112 | + if (is_read_only_access()) { |
| 113 | + return {0, false}; |
| 114 | + } |
| 115 | + |
| 116 | + std::optional<Account> const &prestate_account = |
| 117 | + get_account_for_trace(*prestate); |
| 118 | + std::optional<Account> const &modified_account = |
| 119 | + get_account_for_trace(*modified_state); |
| 120 | + |
| 121 | + uint256_t const prestate_balance = |
| 122 | + is_dead(prestate_account) ? 0 : prestate_account->balance; |
| 123 | + uint256_t const modified_balance = |
| 124 | + is_dead(modified_account) ? 0 : modified_account->balance; |
| 125 | + return {modified_balance, prestate_balance != modified_balance}; |
| 126 | + } |
| 127 | +}; |
| 128 | + |
| 129 | +/// Reserves either a block-level or transaction-level event, depending on |
| 130 | +/// whether opt_txn_num is set or not; the account access events are allocated |
| 131 | +/// this way, as some of them occur at system scope |
| 132 | +template <typename T> |
| 133 | +ReservedExecEvent<T> reserve_event( |
| 134 | + ExecutionEventRecorder *exec_recorder, monad_exec_event_type event_type, |
| 135 | + std::optional<uint32_t> opt_txn_num) |
| 136 | +{ |
| 137 | + return opt_txn_num |
| 138 | + ? exec_recorder->reserve_txn_event<T>(event_type, *opt_txn_num) |
| 139 | + : exec_recorder->reserve_block_event<T>(event_type); |
| 140 | +} |
| 141 | + |
| 142 | +// Records a MONAD_EXEC_STORAGE_ACCESS event for all reads and writes in the |
| 143 | +// AccountState prestate and modified maps |
| 144 | +void record_storage_events( |
| 145 | + ExecutionEventRecorder *exec_recorder, |
| 146 | + monad_exec_account_access_context ctx, std::optional<uint32_t> opt_txn_num, |
| 147 | + uint32_t account_index, Address const *address, |
| 148 | + AccountState::StorageMap const *prestate_storage, |
| 149 | + AccountState::StorageMap const *modified_storage, bool is_transient) |
| 150 | +{ |
| 151 | + for (size_t index = 0; auto const &[key, value] : *prestate_storage) { |
| 152 | + bool is_modified = false; |
| 153 | + bytes32_t end_value = {}; |
| 154 | + |
| 155 | + if (modified_storage) { |
| 156 | + if (bytes32_t const *const v = modified_storage->find(key)) { |
| 157 | + end_value = *v; |
| 158 | + is_modified = end_value != value; |
| 159 | + } |
| 160 | + } |
| 161 | + |
| 162 | + ReservedExecEvent const storage_access = |
| 163 | + reserve_event<monad_exec_storage_access>( |
| 164 | + exec_recorder, MONAD_EXEC_STORAGE_ACCESS, opt_txn_num); |
| 165 | + *storage_access.payload = monad_exec_storage_access{ |
| 166 | + .address = *address, |
| 167 | + .index = static_cast<uint32_t>(index), |
| 168 | + .access_context = ctx, |
| 169 | + .modified = is_modified, |
| 170 | + .transient = is_transient, |
| 171 | + .key = key, |
| 172 | + .start_value = value, |
| 173 | + .end_value = end_value, |
| 174 | + }; |
| 175 | + storage_access.event->content_ext[MONAD_FLOW_ACCOUNT_INDEX] = |
| 176 | + account_index; |
| 177 | + exec_recorder->commit(storage_access); |
| 178 | + ++index; |
| 179 | + } |
| 180 | +} |
| 181 | + |
| 182 | +// Records an MONAD_EXEC_ACCOUNT_ACCESS event, and delegates to |
| 183 | +// record_storage_events to record both the ordinary and transient storage |
| 184 | +// accesses |
| 185 | +void record_account_events( |
| 186 | + ExecutionEventRecorder *exec_recorder, |
| 187 | + monad_exec_account_access_context ctx, std::optional<uint32_t> opt_txn_num, |
| 188 | + uint32_t index, AccountAccessInfo const &account_info) |
| 189 | +{ |
| 190 | + MONAD_ASSERT(account_info.prestate); |
| 191 | + monad_c_eth_account_state initial_state; |
| 192 | + std::optional<Account> const &prestate_account = |
| 193 | + get_account_for_trace(*account_info.prestate); |
| 194 | + bool const prestate_valid = !is_dead(prestate_account); |
| 195 | + |
| 196 | + initial_state.nonce = prestate_valid ? prestate_account->nonce : 0; |
| 197 | + initial_state.balance = prestate_valid ? prestate_account->balance : 0; |
| 198 | + initial_state.code_hash = |
| 199 | + prestate_valid ? prestate_account->code_hash : NULL_HASH; |
| 200 | + |
| 201 | + auto const [modified_balance, is_balance_modified] = |
| 202 | + account_info.get_balance_modification(); |
| 203 | + auto const [modified_nonce, is_nonce_modified] = |
| 204 | + account_info.get_nonce_modification(); |
| 205 | + |
| 206 | + ReservedExecEvent const account_access = |
| 207 | + reserve_event<monad_exec_account_access>( |
| 208 | + exec_recorder, MONAD_EXEC_ACCOUNT_ACCESS, opt_txn_num); |
| 209 | + *account_access.payload = monad_exec_account_access{ |
| 210 | + .index = index, |
| 211 | + .address = *account_info.address, |
| 212 | + .access_context = ctx, |
| 213 | + .is_balance_modified = is_balance_modified, |
| 214 | + .is_nonce_modified = is_nonce_modified, |
| 215 | + .prestate = initial_state, |
| 216 | + .modified_balance = modified_balance, |
| 217 | + .modified_nonce = modified_nonce, |
| 218 | + .storage_key_count = |
| 219 | + static_cast<uint32_t>(size(account_info.prestate->storage_)), |
| 220 | + .transient_count = static_cast<uint32_t>( |
| 221 | + size(account_info.prestate->transient_storage_))}; |
| 222 | + exec_recorder->commit(account_access); |
| 223 | + |
| 224 | + auto const *const post_state_storage_map = |
| 225 | + account_info.is_read_only_access() |
| 226 | + ? nullptr |
| 227 | + : &account_info.modified_state->storage_; |
| 228 | + record_storage_events( |
| 229 | + exec_recorder, |
| 230 | + ctx, |
| 231 | + opt_txn_num, |
| 232 | + index, |
| 233 | + account_info.address, |
| 234 | + &account_info.prestate->storage_, |
| 235 | + post_state_storage_map, |
| 236 | + false); |
| 237 | + |
| 238 | + auto const *const post_state_transient_map = |
| 239 | + account_info.is_read_only_access() |
| 240 | + ? nullptr |
| 241 | + : &account_info.modified_state->transient_storage_; |
| 242 | + record_storage_events( |
| 243 | + exec_recorder, |
| 244 | + ctx, |
| 245 | + opt_txn_num, |
| 246 | + index, |
| 247 | + account_info.address, |
| 248 | + &account_info.prestate->transient_storage_, |
| 249 | + post_state_transient_map, |
| 250 | + true); |
| 251 | +} |
| 252 | + |
| 253 | +// Function that records all state accesses and changes that occurred in some |
| 254 | +// scope, either the block prologue, block epilogue, or in the scope of some |
| 255 | +// transaction |
| 256 | +void record_account_access_events_internal( |
| 257 | + ExecutionEventRecorder *exec_recorder, |
| 258 | + monad_exec_account_access_context ctx, std::optional<uint32_t> opt_txn_num, |
| 259 | + State const &state) |
| 260 | +{ |
| 261 | + auto const &prestate_map = state.original(); |
| 262 | + |
| 263 | + ReservedExecEvent const list_header = |
| 264 | + reserve_event<monad_exec_account_access_list_header>( |
| 265 | + exec_recorder, MONAD_EXEC_ACCOUNT_ACCESS_LIST_HEADER, opt_txn_num); |
| 266 | + *list_header.payload = monad_exec_account_access_list_header{ |
| 267 | + .entry_count = static_cast<uint32_t>(prestate_map.size()), |
| 268 | + .access_context = ctx}; |
| 269 | + exec_recorder->commit(list_header); |
| 270 | + |
| 271 | + auto const ¤t_state_map = state.current(); |
| 272 | + for (uint32_t index = 0; auto const &[address, prestate] : prestate_map) { |
| 273 | + AccountState const *current_state = nullptr; |
| 274 | + if (auto const i = current_state_map.find(address); |
| 275 | + i != end(current_state_map)) { |
| 276 | + current_state = std::addressof(i->second.recent()); |
| 277 | + } |
| 278 | + record_account_events( |
| 279 | + exec_recorder, |
| 280 | + ctx, |
| 281 | + opt_txn_num, |
| 282 | + index, |
| 283 | + AccountAccessInfo{&address, &prestate, current_state}); |
| 284 | + index++; |
| 285 | + } |
| 286 | +} |
| 287 | + |
70 | 288 | MONAD_ANONYMOUS_NAMESPACE_END |
71 | 289 |
|
72 | 290 | MONAD_NAMESPACE_BEGIN |
@@ -136,7 +354,7 @@ void record_txn_header_events( |
136 | 354 |
|
137 | 355 | void record_txn_output_events( |
138 | 356 | uint32_t const txn_num, Receipt const &receipt, |
139 | | - std::span<CallFrame const> const call_frames) |
| 357 | + std::span<CallFrame const> const call_frames, State const &txn_state) |
140 | 358 | { |
141 | 359 | ExecutionEventRecorder *const exec_recorder = g_exec_event_recorder.get(); |
142 | 360 | if (exec_recorder == nullptr) { |
@@ -203,6 +421,10 @@ void record_txn_output_events( |
203 | 421 | ++index; |
204 | 422 | } |
205 | 423 |
|
| 424 | + // Account access records for the transaction |
| 425 | + record_account_access_events_internal( |
| 426 | + exec_recorder, MONAD_ACCT_ACCESS_TRANSACTION, txn_num, txn_state); |
| 427 | + |
206 | 428 | exec_recorder->record_txn_marker_event(MONAD_EXEC_TXN_END, txn_num); |
207 | 429 | } |
208 | 430 |
|
@@ -241,4 +463,16 @@ void record_txn_error_event( |
241 | 463 | } |
242 | 464 | } |
243 | 465 |
|
| 466 | +// The externally-visible wrapper of the account-access-recording function that |
| 467 | +// is called from execute_block.cpp, to record prologue and epilogue accesses; |
| 468 | +// transaction-scope state accesses use record_txn_output_events instead |
| 469 | +void record_account_access_events( |
| 470 | + monad_exec_account_access_context ctx, State const &state) |
| 471 | +{ |
| 472 | + if (ExecutionEventRecorder *const e = g_exec_event_recorder.get()) { |
| 473 | + return record_account_access_events_internal( |
| 474 | + e, ctx, std::nullopt, state); |
| 475 | + } |
| 476 | +} |
| 477 | + |
244 | 478 | MONAD_NAMESPACE_END |
0 commit comments