Skip to content

Commit 8bb9232

Browse files
committed
[event] record state access events
There are a few changes here: - The ExecuteTransaction and ExecuteSystemTransaction functors are given a State object rather than creating one, so it can be owned by the outer transaction fiber. After the functor is done executing the transaction, this outer fiber gives the State to the recording code - `class State` gains the `State::clear` method, so that it can be reset when re-execution is needed - record_txn_events.cpp gains functions for recording State objects as `ACCOUNT_ACCESS_LIST_HEADER`, `ACCOUNT_ACCESS`, and `STORAGE_ACCESS` events - All state-affecting changes to the block that occur before transaction and after the transaction are also recorded
1 parent d5a471f commit 8bb9232

15 files changed

+329
-58
lines changed

category/execution/ethereum/dispatch_transaction.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Result<Receipt> dispatch_transaction(
2525
Address const &sender,
2626
std::vector<std::optional<Address>> const &authorities,
2727
BlockHeader const &header, BlockHashBuffer const &block_hash_buffer,
28-
BlockState &block_state, BlockMetrics &block_metrics,
28+
BlockMetrics &block_metrics, State &state,
2929
boost::fibers::promise<void> &prev, CallTracerBase &call_tracer,
3030
trace::StateTracer &state_tracer,
3131
RevertTransactionFn const &revert_transaction)
@@ -38,8 +38,8 @@ Result<Receipt> dispatch_transaction(
3838
authorities,
3939
header,
4040
block_hash_buffer,
41-
block_state,
4241
block_metrics,
42+
state,
4343
prev,
4444
call_tracer,
4545
state_tracer,

category/execution/ethereum/dispatch_transaction.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ MONAD_NAMESPACE_BEGIN
3434

3535
class BlockHashBuffer;
3636
class BlockMetrics;
37-
class BlockState;
3837
class State;
3938
struct BlockHeader;
4039
struct CallTracerBase;
@@ -51,7 +50,7 @@ Result<Receipt> dispatch_transaction(
5150
Address const &sender,
5251
std::vector<std::optional<Address>> const &authorities,
5352
BlockHeader const &header, BlockHashBuffer const &block_hash_buffer,
54-
BlockState &block_state, BlockMetrics &block_metrics,
53+
BlockMetrics &block_metrics, State &state,
5554
boost::fibers::promise<void> &prev, CallTracerBase &call_tracer,
5655
trace::StateTracer &state_tracer,
5756
RevertTransactionFn const &revert_transaction);

category/execution/ethereum/event/record_txn_events.cpp

Lines changed: 228 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313
// You should have received a copy of the GNU General Public License
1414
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1515

16+
#include <category/core/assert.h>
1617
#include <category/core/bytes.hpp>
1718
#include <category/core/config.hpp>
19+
#include <category/core/int.hpp>
1820
#include <category/core/keccak.hpp>
1921
#include <category/core/result.hpp>
22+
#include <category/execution/ethereum/core/account.hpp>
2023
#include <category/execution/ethereum/core/address.hpp>
2124
#include <category/execution/ethereum/core/eth_ctypes.h>
2225
#include <category/execution/ethereum/core/rlp/transaction_rlp.hpp>
@@ -25,11 +28,16 @@
2528
#include <category/execution/ethereum/event/exec_event_recorder.hpp>
2629
#include <category/execution/ethereum/event/record_txn_events.hpp>
2730
#include <category/execution/ethereum/execute_transaction.hpp>
31+
#include <category/execution/ethereum/state3/account_state.hpp>
32+
#include <category/execution/ethereum/state3/state.hpp>
33+
#include <category/execution/ethereum/state3/version_stack.hpp>
2834
#include <category/execution/ethereum/trace/call_frame.hpp>
2935
#include <category/execution/ethereum/validate_transaction.hpp>
3036

3137
#include <bit>
38+
#include <cstddef>
3239
#include <cstdint>
40+
#include <memory>
3341
#include <optional>
3442
#include <span>
3543
#include <utility>
@@ -66,6 +74,209 @@ void init_txn_header_start(
6674
static_cast<uint32_t>(txn.authorization_list.size());
6775
}
6876

77+
// Tracks information about an accessed account, including (1) the prestate and
78+
// the (2) the modified state if a write access modified anything, with helper
79+
// functions to determine what was modified
80+
struct AccountAccessInfo
81+
{
82+
Address const *address;
83+
OriginalAccountState const *prestate; // State as it existed in original
84+
AccountState const *modified_state; // Last state as it existed in current
85+
86+
bool is_read_only_access() const
87+
{
88+
return modified_state == nullptr;
89+
}
90+
91+
std::pair<uint64_t, bool> get_nonce_modification() const
92+
{
93+
if (is_read_only_access()) {
94+
return {0, false};
95+
}
96+
uint64_t const prestate_nonce =
97+
is_dead(prestate->account_) ? 0 : prestate->account_->nonce;
98+
uint64_t const modified_nonce = is_dead(modified_state->account_)
99+
? 0
100+
: modified_state->account_->nonce;
101+
return {modified_nonce, prestate_nonce != modified_nonce};
102+
}
103+
104+
std::pair<uint256_t, bool> get_balance_modification() const
105+
{
106+
if (is_read_only_access()) {
107+
return {0, false};
108+
}
109+
110+
uint256_t const prestate_balance =
111+
is_dead(prestate->account_) ? 0 : prestate->account_->balance;
112+
uint256_t const modified_balance =
113+
is_dead(modified_state->account_)
114+
? 0
115+
: modified_state->account_->balance;
116+
return {modified_balance, prestate_balance != modified_balance};
117+
}
118+
};
119+
120+
/// Reserves either a block-level or transaction-level event, depending on
121+
/// whether opt_txn_num is set or not; the account access events are allocated
122+
/// this way, as some of them occur at system scope
123+
template <typename T>
124+
ReservedExecEvent<T> reserve_event(
125+
ExecutionEventRecorder *exec_recorder, monad_exec_event_type event_type,
126+
std::optional<uint32_t> opt_txn_num)
127+
{
128+
return opt_txn_num
129+
? exec_recorder->reserve_txn_event<T>(event_type, *opt_txn_num)
130+
: exec_recorder->reserve_block_event<T>(event_type);
131+
}
132+
133+
// Records a MONAD_EXEC_STORAGE_ACCESS event for all reads and writes in the
134+
// AccountState prestate and modified maps
135+
void record_storage_events(
136+
ExecutionEventRecorder *exec_recorder,
137+
monad_exec_account_access_context ctx, std::optional<uint32_t> opt_txn_num,
138+
uint32_t account_index, Address const *address,
139+
AccountState::Map<bytes32_t, bytes32_t> const *prestate_storage,
140+
AccountState::Map<bytes32_t, bytes32_t> const *modified_storage,
141+
bool is_transient)
142+
{
143+
for (size_t index = 0; auto const &[key, value] : *prestate_storage) {
144+
bool is_modified = false;
145+
bytes32_t end_value = {};
146+
147+
if (modified_storage) {
148+
if (auto const i = modified_storage->find(key);
149+
i != end(*modified_storage)) {
150+
end_value = i->second;
151+
is_modified = end_value != value;
152+
}
153+
}
154+
155+
ReservedExecEvent const storage_access =
156+
reserve_event<monad_exec_storage_access>(
157+
exec_recorder, MONAD_EXEC_STORAGE_ACCESS, opt_txn_num);
158+
*storage_access.payload = monad_exec_storage_access{
159+
.address = *address,
160+
.index = static_cast<uint32_t>(index),
161+
.access_context = ctx,
162+
.modified = is_modified,
163+
.transient = is_transient,
164+
.key = key,
165+
.start_value = value,
166+
.end_value = end_value,
167+
};
168+
storage_access.event->content_ext[MONAD_FLOW_ACCOUNT_INDEX] =
169+
account_index;
170+
exec_recorder->commit(storage_access);
171+
++index;
172+
}
173+
}
174+
175+
// Records an MONAD_EXEC_ACCOUNT_ACCESS event, and delegates to
176+
// record_storage_events to record both the ordinary and transient storage
177+
// accesses
178+
void record_account_events(
179+
ExecutionEventRecorder *exec_recorder,
180+
monad_exec_account_access_context ctx, std::optional<uint32_t> opt_txn_num,
181+
uint32_t index, AccountAccessInfo const &account_info)
182+
{
183+
MONAD_ASSERT(account_info.prestate);
184+
monad_c_eth_account_state initial_state;
185+
Account const &account = is_dead(account_info.prestate->account_)
186+
? Account{}
187+
: *account_info.prestate->account_;
188+
189+
initial_state.nonce = account.nonce;
190+
initial_state.balance = account.balance;
191+
initial_state.code_hash = account.code_hash;
192+
193+
auto const [modified_balance, is_balance_modified] =
194+
account_info.get_balance_modification();
195+
auto const [modified_nonce, is_nonce_modified] =
196+
account_info.get_nonce_modification();
197+
198+
ReservedExecEvent const account_access =
199+
reserve_event<monad_exec_account_access>(
200+
exec_recorder, MONAD_EXEC_ACCOUNT_ACCESS, opt_txn_num);
201+
*account_access.payload = monad_exec_account_access{
202+
.index = index,
203+
.address = *account_info.address,
204+
.access_context = ctx,
205+
.is_balance_modified = is_balance_modified,
206+
.is_nonce_modified = is_nonce_modified,
207+
.prestate = initial_state,
208+
.modified_balance = modified_balance,
209+
.modified_nonce = modified_nonce,
210+
.storage_key_count =
211+
static_cast<uint32_t>(size(account_info.prestate->storage_)),
212+
.transient_count = static_cast<uint32_t>(
213+
size(account_info.prestate->transient_storage_))};
214+
exec_recorder->commit(account_access);
215+
216+
auto const *const post_state_storage_map =
217+
account_info.is_read_only_access()
218+
? nullptr
219+
: &account_info.modified_state->storage_;
220+
record_storage_events(
221+
exec_recorder,
222+
ctx,
223+
opt_txn_num,
224+
index,
225+
account_info.address,
226+
&account_info.prestate->storage_,
227+
post_state_storage_map,
228+
false);
229+
230+
auto const *const post_state_transient_map =
231+
account_info.is_read_only_access()
232+
? nullptr
233+
: &account_info.modified_state->transient_storage_;
234+
record_storage_events(
235+
exec_recorder,
236+
ctx,
237+
opt_txn_num,
238+
index,
239+
account_info.address,
240+
&account_info.prestate->transient_storage_,
241+
post_state_transient_map,
242+
true);
243+
}
244+
245+
// Function that records all state accesses and changes that occurred in some
246+
// scope, either the block prologue, block epilogue, or in the scope of some
247+
// transaction
248+
void record_account_access_events_internal(
249+
ExecutionEventRecorder *exec_recorder,
250+
monad_exec_account_access_context ctx, std::optional<uint32_t> opt_txn_num,
251+
State const &state)
252+
{
253+
auto const &prestate_map = state.original();
254+
255+
ReservedExecEvent const list_header =
256+
reserve_event<monad_exec_account_access_list_header>(
257+
exec_recorder, MONAD_EXEC_ACCOUNT_ACCESS_LIST_HEADER, opt_txn_num);
258+
*list_header.payload = monad_exec_account_access_list_header{
259+
.entry_count = static_cast<uint32_t>(prestate_map.size()),
260+
.access_context = ctx};
261+
exec_recorder->commit(list_header);
262+
263+
auto const &current_state_map = state.current();
264+
for (uint32_t index = 0; auto const &[address, prestate] : prestate_map) {
265+
AccountState const *current_state = nullptr;
266+
if (auto const i = current_state_map.find(address);
267+
i != end(current_state_map)) {
268+
current_state = std::addressof(i->second.recent());
269+
}
270+
record_account_events(
271+
exec_recorder,
272+
ctx,
273+
opt_txn_num,
274+
index,
275+
AccountAccessInfo{&address, &prestate, current_state});
276+
index++;
277+
}
278+
}
279+
69280
MONAD_ANONYMOUS_NAMESPACE_END
70281

71282
MONAD_NAMESPACE_BEGIN
@@ -74,7 +285,7 @@ void record_txn_events(
74285
uint32_t txn_num, Transaction const &transaction, Address const &sender,
75286
std::span<std::optional<Address> const> authorities,
76287
Result<Receipt> const &receipt_result,
77-
std::span<CallFrame const> const call_frames)
288+
std::span<CallFrame const> const call_frames, State const &txn_state)
78289
{
79290
ExecutionEventRecorder *const exec_recorder = g_exec_event_recorder.get();
80291
if (exec_recorder == nullptr) {
@@ -223,7 +434,23 @@ void record_txn_events(
223434
++index;
224435
}
225436

437+
// Account access records for the transaction
438+
record_account_access_events_internal(
439+
exec_recorder, MONAD_ACCT_ACCESS_TRANSACTION, txn_num, txn_state);
440+
226441
exec_recorder->record_txn_marker_event(MONAD_EXEC_TXN_END, txn_num);
227442
}
228443

444+
// The externally-visible wrapper of the account-access-recording function that
445+
// is called from execute_block.cpp, to record prologue and epilogue accesses;
446+
// transaction-scope state accesses use record_txn_exec_result_events instead
447+
void record_account_access_events(
448+
monad_exec_account_access_context ctx, State const &state)
449+
{
450+
if (ExecutionEventRecorder *const e = g_exec_event_recorder.get()) {
451+
return record_account_access_events_internal(
452+
e, ctx, std::nullopt, state);
453+
}
454+
}
455+
229456
MONAD_NAMESPACE_END

category/execution/ethereum/event/record_txn_events.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@
2323
#include <optional>
2424
#include <span>
2525

26+
enum monad_exec_account_access_context : uint8_t;
27+
2628
MONAD_NAMESPACE_BEGIN
2729

2830
struct CallFrame;
2931
struct Receipt;
3032
struct Transaction;
3133

34+
class State;
35+
3236
/// Record the transaction header events (TXN_HEADER_START, the EIP-2930
3337
/// and EIP-7702 events, and TXN_HEADER_END), followed by the TXN_EVM_OUTPUT,
3438
/// TXN_REJECT, or EVM_ERROR events, depending on what happened during
@@ -37,6 +41,11 @@ struct Transaction;
3741
void record_txn_events(
3842
uint32_t txn_num, Transaction const &, Address const &sender,
3943
std::span<std::optional<Address> const> authorities,
40-
Result<Receipt> const &, std::span<CallFrame const>);
44+
Result<Receipt> const &, std::span<CallFrame const>, State const &);
45+
46+
/// Record all account state accesses (both reads and writes) described by a
47+
/// State object
48+
void record_account_access_events(
49+
monad_exec_account_access_context, State const &);
4150

4251
MONAD_NAMESPACE_END

category/execution/ethereum/execute_block.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ void execute_block_header(
206206

207207
MONAD_ASSERT(block_state.can_merge(state));
208208
block_state.merge(state);
209+
record_account_access_events(MONAD_ACCT_ACCESS_BLOCK_PROLOGUE, state);
209210
}
210211

211212
EXPLICIT_TRAITS(execute_block_header);
@@ -255,6 +256,7 @@ Result<std::vector<Receipt>> execute_block_transactions(
255256
&txn_exec_finished,
256257
&revert_transaction = revert_transaction] {
257258
record_txn_marker_event(MONAD_EXEC_TXN_PERF_EVM_ENTER, i);
259+
State state{block_state, Incarnation{header.number, i + 1}};
258260
try {
259261
results[i] = dispatch_transaction<traits>(
260262
chain,
@@ -264,8 +266,8 @@ Result<std::vector<Receipt>> execute_block_transactions(
264266
authorities,
265267
header,
266268
block_hash_buffer,
267-
block_state,
268269
block_metrics,
270+
state,
269271
promises[i],
270272
call_tracer,
271273
state_tracer,
@@ -278,7 +280,8 @@ Result<std::vector<Receipt>> execute_block_transactions(
278280
sender,
279281
authorities,
280282
*results[i],
281-
call_tracer.get_call_frames());
283+
call_tracer.get_call_frames(),
284+
state);
282285
}
283286
catch (...) {
284287
promises[i + 1].set_exception(std::current_exception());
@@ -296,8 +299,8 @@ Result<std::vector<Receipt>> execute_block_transactions(
296299
// All transactions have released their merge-order synchronization
297300
// primitive (promises[i + 1]) but some stragglers could still be running
298301
// post-execution code that occurs immediately after that, e.g.
299-
// `record_txn_exec_result_events`. This waits for everything to finish
300-
// so that it's safe to assume we're the only ones using `results`.
302+
// `record_txn_events`. This waits for everything to finish so that it's
303+
// safe to assume we're the only ones using `results`.
301304
while (txn_exec_finished.load() < txn_count) {
302305
cpu_relax();
303306
}
@@ -377,6 +380,7 @@ Result<std::vector<Receipt>> execute_block(
377380

378381
MONAD_ASSERT(block_state.can_merge(state));
379382
block_state.merge(state);
383+
record_account_access_events(MONAD_ACCT_ACCESS_BLOCK_EPILOGUE, state);
380384

381385
return retvals;
382386
}

0 commit comments

Comments
 (0)