Skip to content

Commit a05333c

Browse files
author
AztecBot
committed
Merge branch 'next' into merge-train/barretenberg
2 parents 1789e3b + 152eadf commit a05333c

File tree

7 files changed

+165
-130
lines changed

7 files changed

+165
-130
lines changed

barretenberg/cpp/src/barretenberg/vm2/common/aztec_types.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ enum TransactionPhase {
2929
COLLECT_GAS_FEES = 10,
3030
TREE_PADDING = 11,
3131
CLEANUP = 12,
32+
LAST = CLEANUP,
3233
};
3334

3435
using InternalCallId = uint32_t;

barretenberg/cpp/src/barretenberg/vm2/simulation/events/tx_events.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,15 @@ struct PadTreesEvent {};
4848

4949
struct CleanupEvent {};
5050

51+
struct EmptyPhaseEvent {};
52+
5153
using TxPhaseEventType = std::variant<EnqueuedCallEvent,
5254
PrivateAppendTreeEvent,
5355
PrivateEmitL2L1MessageEvent,
5456
CollectGasFeeEvent,
5557
PadTreesEvent,
56-
CleanupEvent>;
58+
CleanupEvent,
59+
EmptyPhaseEvent>;
5760

5861
struct TxPhaseEvent {
5962
TransactionPhase phase;

barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.cpp

Lines changed: 105 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -83,50 +83,11 @@ void TxExecution::simulate(const Tx& tx)
8383
insert_non_revertibles(tx);
8484

8585
// Setup.
86-
for (const auto& call : tx.setupEnqueuedCalls) {
87-
info("[SETUP] Executing enqueued call to ", call.request.contractAddress);
88-
TxContextEvent state_before = tx_context.serialize_tx_context_event();
89-
Gas start_gas = tx_context.gas_used;
90-
auto context = context_provider.make_enqueued_context(call.request.contractAddress,
91-
call.request.msgSender,
92-
/*transaction_fee=*/FF(0),
93-
call.calldata,
94-
call.request.isStaticCall,
95-
gas_limit,
96-
start_gas,
97-
tx_context.side_effect_states,
98-
TransactionPhase::SETUP);
99-
// This call should not throw unless it's an unexpected unrecoverable failure.
100-
ExecutionResult result = call_execution.execute(std::move(context));
101-
tx_context.side_effect_states = result.side_effect_states;
102-
tx_context.gas_used = result.gas_used;
103-
emit_public_call_request(call,
104-
TransactionPhase::SETUP,
105-
/*transaction_fee=*/FF(0),
106-
result.success,
107-
start_gas,
108-
tx_context.gas_used,
109-
state_before,
110-
tx_context.serialize_tx_context_event());
111-
if (!result.success) {
112-
// This will result in an unprovable tx.
113-
throw TxExecutionException(
114-
format("[SETUP] UNRECOVERABLE ERROR! Enqueued call to ", call.request.contractAddress, " failed"));
115-
}
116-
}
117-
SideEffectStates end_setup_side_effect_states = tx_context.side_effect_states;
118-
119-
// The checkpoint we should go back to if anything from now on reverts.
120-
merkle_db.create_checkpoint();
121-
122-
try {
123-
// Insert revertibles. This can throw if there is a nullifier collision.
124-
// Such an exception should be handled and the tx be provable.
125-
insert_revertibles(tx);
126-
127-
// App logic.
128-
for (const auto& call : tx.appLogicEnqueuedCalls) {
129-
info("[APP_LOGIC] Executing enqueued call to ", call.request.contractAddress);
86+
if (tx.setupEnqueuedCalls.empty()) {
87+
emit_empty_phase(TransactionPhase::SETUP);
88+
} else {
89+
for (const auto& call : tx.setupEnqueuedCalls) {
90+
info("[SETUP] Executing enqueued call to ", call.request.contractAddress);
13091
TxContextEvent state_before = tx_context.serialize_tx_context_event();
13192
Gas start_gas = tx_context.gas_used;
13293
auto context = context_provider.make_enqueued_context(call.request.contractAddress,
@@ -137,23 +98,70 @@ void TxExecution::simulate(const Tx& tx)
13798
gas_limit,
13899
start_gas,
139100
tx_context.side_effect_states,
140-
TransactionPhase::APP_LOGIC);
101+
TransactionPhase::SETUP);
141102
// This call should not throw unless it's an unexpected unrecoverable failure.
142103
ExecutionResult result = call_execution.execute(std::move(context));
143104
tx_context.side_effect_states = result.side_effect_states;
144105
tx_context.gas_used = result.gas_used;
145106
emit_public_call_request(call,
146-
TransactionPhase::APP_LOGIC,
107+
TransactionPhase::SETUP,
147108
/*transaction_fee=*/FF(0),
148109
result.success,
149110
start_gas,
150111
tx_context.gas_used,
151112
state_before,
152113
tx_context.serialize_tx_context_event());
153114
if (!result.success) {
154-
// This exception should be handled and the tx be provable.
115+
// This will result in an unprovable tx.
155116
throw TxExecutionException(
156-
format("[APP_LOGIC] Enqueued call to ", call.request.contractAddress, " failed"));
117+
format("[SETUP] UNRECOVERABLE ERROR! Enqueued call to ", call.request.contractAddress, " failed"));
118+
}
119+
}
120+
}
121+
SideEffectStates end_setup_side_effect_states = tx_context.side_effect_states;
122+
123+
// The checkpoint we should go back to if anything from now on reverts.
124+
merkle_db.create_checkpoint();
125+
126+
try {
127+
// Insert revertibles. This can throw if there is a nullifier collision.
128+
// Such an exception should be handled and the tx be provable.
129+
insert_revertibles(tx);
130+
131+
// App logic.
132+
if (tx.appLogicEnqueuedCalls.empty()) {
133+
emit_empty_phase(TransactionPhase::APP_LOGIC);
134+
} else {
135+
for (const auto& call : tx.appLogicEnqueuedCalls) {
136+
info("[APP_LOGIC] Executing enqueued call to ", call.request.contractAddress);
137+
TxContextEvent state_before = tx_context.serialize_tx_context_event();
138+
Gas start_gas = tx_context.gas_used;
139+
auto context = context_provider.make_enqueued_context(call.request.contractAddress,
140+
call.request.msgSender,
141+
/*transaction_fee=*/FF(0),
142+
call.calldata,
143+
call.request.isStaticCall,
144+
gas_limit,
145+
start_gas,
146+
tx_context.side_effect_states,
147+
TransactionPhase::APP_LOGIC);
148+
// This call should not throw unless it's an unexpected unrecoverable failure.
149+
ExecutionResult result = call_execution.execute(std::move(context));
150+
tx_context.side_effect_states = result.side_effect_states;
151+
tx_context.gas_used = result.gas_used;
152+
emit_public_call_request(call,
153+
TransactionPhase::APP_LOGIC,
154+
/*transaction_fee=*/FF(0),
155+
result.success,
156+
start_gas,
157+
tx_context.gas_used,
158+
state_before,
159+
tx_context.serialize_tx_context_event());
160+
if (!result.success) {
161+
// This exception should be handled and the tx be provable.
162+
throw TxExecutionException(
163+
format("[APP_LOGIC] Enqueued call to ", call.request.contractAddress, " failed"));
164+
}
157165
}
158166
}
159167
} catch (const TxExecutionException& e) {
@@ -175,7 +183,9 @@ void TxExecution::simulate(const Tx& tx)
175183

176184
// Teardown.
177185
try {
178-
if (tx.teardownEnqueuedCall) {
186+
if (!tx.teardownEnqueuedCall) {
187+
emit_empty_phase(TransactionPhase::TEARDOWN);
188+
} else {
179189
info("[TEARDOWN] Executing enqueued call to ", tx.teardownEnqueuedCall->request.contractAddress);
180190
// Teardown has its own gas limit and usage.
181191
Gas start_gas = { 0, 0 };
@@ -334,18 +344,30 @@ void TxExecution::insert_non_revertibles(const Tx& tx)
334344
tx.hash);
335345

336346
// 1. Write the already siloed nullifiers.
337-
for (const auto& nullifier : tx.nonRevertibleAccumulatedData.nullifiers) {
338-
emit_nullifier(false, nullifier);
347+
if (tx.nonRevertibleAccumulatedData.nullifiers.empty()) {
348+
emit_empty_phase(TransactionPhase::NR_NULLIFIER_INSERTION);
349+
} else {
350+
for (const auto& nullifier : tx.nonRevertibleAccumulatedData.nullifiers) {
351+
emit_nullifier(false, nullifier);
352+
}
339353
}
340354

341355
// 2. Write already unique note hashes.
342-
for (const auto& unique_note_hash : tx.nonRevertibleAccumulatedData.noteHashes) {
343-
emit_note_hash(false, unique_note_hash);
356+
if (tx.nonRevertibleAccumulatedData.noteHashes.empty()) {
357+
emit_empty_phase(TransactionPhase::NR_NOTE_INSERTION);
358+
} else {
359+
for (const auto& unique_note_hash : tx.nonRevertibleAccumulatedData.noteHashes) {
360+
emit_note_hash(false, unique_note_hash);
361+
}
344362
}
345363

346364
// 3. Write l2_l1 messages
347-
for (const auto& l2_to_l1_msg : tx.nonRevertibleAccumulatedData.l2ToL1Messages) {
348-
emit_l2_to_l1_message(false, l2_to_l1_msg);
365+
if (tx.nonRevertibleAccumulatedData.l2ToL1Messages.empty()) {
366+
emit_empty_phase(TransactionPhase::NR_L2_TO_L1_MESSAGE);
367+
} else {
368+
for (const auto& l2_to_l1_msg : tx.nonRevertibleAccumulatedData.l2ToL1Messages) {
369+
emit_l2_to_l1_message(false, l2_to_l1_msg);
370+
}
349371
}
350372
}
351373

@@ -362,18 +384,30 @@ void TxExecution::insert_revertibles(const Tx& tx)
362384
tx.hash);
363385

364386
// 1. Write the already siloed nullifiers.
365-
for (const auto& siloed_nullifier : tx.revertibleAccumulatedData.nullifiers) {
366-
emit_nullifier(true, siloed_nullifier);
387+
if (tx.revertibleAccumulatedData.nullifiers.empty()) {
388+
emit_empty_phase(TransactionPhase::R_NULLIFIER_INSERTION);
389+
} else {
390+
for (const auto& siloed_nullifier : tx.revertibleAccumulatedData.nullifiers) {
391+
emit_nullifier(true, siloed_nullifier);
392+
}
367393
}
368394

369395
// 2. Write the siloed non uniqued note hashes
370-
for (const auto& siloed_note_hash : tx.revertibleAccumulatedData.noteHashes) {
371-
emit_note_hash(true, siloed_note_hash);
396+
if (tx.revertibleAccumulatedData.noteHashes.empty()) {
397+
emit_empty_phase(TransactionPhase::R_NOTE_INSERTION);
398+
} else {
399+
for (const auto& siloed_note_hash : tx.revertibleAccumulatedData.noteHashes) {
400+
emit_note_hash(true, siloed_note_hash);
401+
}
372402
}
373403

374404
// 3. Write L2 to L1 messages.
375-
for (const auto& l2_to_l1_msg : tx.revertibleAccumulatedData.l2ToL1Messages) {
376-
emit_l2_to_l1_message(true, l2_to_l1_msg);
405+
if (tx.revertibleAccumulatedData.l2ToL1Messages.empty()) {
406+
emit_empty_phase(TransactionPhase::R_L2_TO_L1_MESSAGE);
407+
} else {
408+
for (const auto& l2_to_l1_msg : tx.revertibleAccumulatedData.l2ToL1Messages) {
409+
emit_l2_to_l1_message(true, l2_to_l1_msg);
410+
}
377411
}
378412
}
379413

@@ -426,4 +460,14 @@ void TxExecution::cleanup()
426460
.event = CleanupEvent{} });
427461
}
428462

463+
void TxExecution::emit_empty_phase(TransactionPhase phase)
464+
{
465+
TxContextEvent current_state = tx_context.serialize_tx_context_event();
466+
events.emit(TxPhaseEvent{ .phase = phase,
467+
.state_before = current_state,
468+
.state_after = current_state,
469+
.reverted = false,
470+
.event = EmptyPhaseEvent{} });
471+
}
472+
429473
} // namespace bb::avm2::simulation

barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ class TxExecution final {
6666
void pad_trees();
6767

6868
void cleanup();
69+
70+
void emit_empty_phase(TransactionPhase phase);
6971
};
7072

7173
} // namespace bb::avm2::simulation

barretenberg/cpp/src/barretenberg/vm2/tracegen/tx_trace.cpp

Lines changed: 15 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ template <class... Ts> struct overloaded : Ts... {
2828
// explicit deduction guide (not needed as of C++20)
2929
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
3030

31-
constexpr size_t NUM_PHASES = 12; // See TransactionPhase enum
31+
constexpr size_t NUM_PHASES = static_cast<size_t>(TransactionPhase::LAST);
3232

3333
bool is_revertible(TransactionPhase phase)
3434
{
@@ -529,7 +529,6 @@ void TxTraceBuilder::process(const simulation::EventEmitterInterface<simulation:
529529

530530
// This is the tree state we will use during the "skipped" phases
531531
TxContextEvent propagated_state = startup_event_data.state;
532-
TxContextEvent end_setup_snapshot = startup_event_data.state;
533532
// Used to track the gas limit for the "padded" phases.
534533
Gas current_gas_limit = startup_event_data.gas_limit;
535534
Gas teardown_gas_limit = startup_event_data.teardown_gas_limit;
@@ -538,6 +537,12 @@ void TxTraceBuilder::process(const simulation::EventEmitterInterface<simulation:
538537
// Go through each phase except startup and process the events in the phase
539538
for (uint32_t i = 0; i < NUM_PHASES; i++) {
540539
const auto& phase_events = phase_buckets[i];
540+
if (phase_events.empty()) {
541+
// There will be no events for a phase if it is skipped (jumped over) due to a revert.
542+
// This is different from a phase that has an EmptyPhaseEvent, which is a phase that has no contents to
543+
// process, like when app logic starts but has no enqueued calls.
544+
continue;
545+
}
541546

542547
TransactionPhase phase = phase_array[i];
543548

@@ -555,24 +560,6 @@ void TxTraceBuilder::process(const simulation::EventEmitterInterface<simulation:
555560
current_gas_limit = teardown_gas_limit;
556561
}
557562

558-
if (phase_events.empty()) {
559-
trace.set(row, insert_state(propagated_state, propagated_state));
560-
trace.set(row, handle_padded_row(phase, gas_used, discard));
561-
trace.set(row, handle_pi_read(phase, /*phase_length=*/0, /*read_counter*/ 0));
562-
trace.set(row, handle_prev_gas_used(gas_used));
563-
trace.set(row, handle_next_gas_used(gas_used));
564-
trace.set(row, handle_gas_limit(current_gas_limit));
565-
trace.set(row, handle_state_change_selectors(phase));
566-
if (row == 1) {
567-
trace.set(row, handle_first_row());
568-
}
569-
if (phase == TransactionPhase::SETUP) {
570-
// If setup is empty, the end-setup-snapshot should just be current/propagated state
571-
end_setup_snapshot = propagated_state;
572-
}
573-
row++;
574-
continue;
575-
}
576563
// Count the number of steps in this phase
577564
uint32_t phase_counter = 0;
578565
uint32_t phase_length = static_cast<uint32_t>(phase_events.size());
@@ -591,7 +578,7 @@ void TxTraceBuilder::process(const simulation::EventEmitterInterface<simulation:
591578
{ C::tx_discard, discard ? 1 : 0 },
592579
{ C::tx_phase_value, static_cast<uint8_t>(tx_phase_event->phase) },
593580
{ Column::tx_setup_phase_value, static_cast<uint8_t>(TransactionPhase::SETUP) },
594-
{ C::tx_is_padded, 0 },
581+
{ C::tx_is_padded, 0 }, // overidden below if this is a skipped phase event
595582
{ C::tx_start_phase, phase_counter == 0 ? 1 : 0 },
596583
{ C::tx_sel_read_phase_length, phase_counter == 0 && !is_one_shot_phase(tx_phase_event->phase) },
597584
{ C::tx_is_revertible, is_revertible(tx_phase_event->phase) ? 1 : 0 },
@@ -640,40 +627,23 @@ void TxTraceBuilder::process(const simulation::EventEmitterInterface<simulation:
640627
[&](const simulation::CleanupEvent&) {
641628
trace.set(row, handle_pi_read(tx_phase_event->phase, 1, 0));
642629
trace.set(row, handle_cleanup());
630+
},
631+
[&](const simulation::EmptyPhaseEvent&) {
632+
// EmptyPhaseEvent represents a phase that is not explicitly skipped because of a
633+
// revert, but just has no contents to process, like when app logic starts but has no
634+
// enqueued calls.
635+
trace.set(row, handle_pi_read(tx_phase_event->phase, 0, 0));
636+
trace.set(row, handle_padded_row(tx_phase_event->phase, gas_used, discard));
643637
} },
644638
tx_phase_event->event);
645639
trace.set(row, handle_next_gas_used(gas_used));
646640
trace.set(row, handle_gas_limit(current_gas_limit));
647641

648-
// Handle a potential phase jump due to a revert, we dont need to check if we are in a revertible phase
649-
// since our witgen will have exited for any reverts in a non-revertible phase.
650-
// If we revert in a phase that isnt TEARDOWN, we jump to TEARDOWN
651-
if (tx_phase_event->reverted && tx_phase_event->phase != TransactionPhase::TEARDOWN) {
652-
// Jump to the TEARDOWN phase
653-
// we need to -2 because of the loop increment and because the enum is 1-indexed
654-
i = static_cast<uint8_t>(TransactionPhase::TEARDOWN) - 2;
655-
}
656642
phase_counter++;
657643
row++;
658644
}
659645
// In case we encounter another skip row
660646
propagated_state = phase_events.back()->state_after;
661-
662-
if (phase == TransactionPhase::SETUP) {
663-
// Store off the state at the end of setup to rollback to later on revert
664-
end_setup_snapshot = phase_events.back()->state_after;
665-
}
666-
if (phase_events.back()->reverted) {
667-
// On revert, roll back to end-setup-snapshot
668-
// Even though tx-execution events should already do this,
669-
// we need to update propagated state here so that any padded rows
670-
// get the correct rolled-back state rather then the pre-rollback state.
671-
propagated_state.tree_states = end_setup_snapshot.tree_states;
672-
propagated_state.written_public_data_slots_tree_snapshot =
673-
end_setup_snapshot.written_public_data_slots_tree_snapshot;
674-
propagated_state.side_effect_states = end_setup_snapshot.side_effect_states;
675-
// Note: we only rollback tree/side-effect states, not gas used or next_context_id.
676-
}
677647
}
678648
}
679649

0 commit comments

Comments
 (0)