Skip to content

Commit

Permalink
feat!: public logs (#11091)
Browse files Browse the repository at this point in the history
## Public Logs

---

Unencrypted logs -> public logs #9589

Like `private_logs`, public logs are introduced in this PR and replace
unencrypted logs. They:

- Are no longer treated as bytes in ts/sol
- Are no longer treated as log hashes in kernels/rollups
- Are treated as arrays of fields (with contract address) everywhere

AVM team: I've made some limited changes with help from Ilyas (tyvm)
just so we have tests passing and logs being emitted, this is not
complete! I've added #11124 to help track where changes need to be made
in areas of the code I have no familiarity with. I didn't want to touch
too many areas so I haven't fully renamed unencrypted -> public. Ofc I'm
happy to help anywhere that's needed.

Aztec-nr/Noir-contracts: This PR also addresses #9835. I don't know much
about how partial notes work or should work, so I tried to touch the
least I could to convert these logs to fields. One big change is that
the first field now contains the length of private fields and ciphertext
bytes along with the public fields. This is because now we don't emit
logs as an array of bytes with a set length to ts, there isn't a way to
tell when a log 'ends'. We also can't just discard zero values, because
in many cases zeros are emitted as real log values.

---

~TODO:~ Completed

- ~Some more renaming (e.g. `UnencryptedLogsResponse`, prefixes, public
context, noir contracts)~
- ~`MAX_UNENCRYPTED_LOGS_PER_CALL` -> `MAX_PUBLIC_LOGS_PER_CALL` (not
done yet, because `PublicCircuitPublicInputs` is linked to
`AvmCircuitInputs` which goes into bb)~
- ~Test and cleanup anything touching partial notes~

---

TODO in follow-up PRS:
- Tightly pack individual logs when adding to blob: This is relatively
complex because of the hacks we have in place (#10323) and the
requirement to overhaul blob field decoding, to avoid bloating this PR
I'll make a new one.
- Rename `emit_unencrypted`: This will touch a lot of files and just
make it difficult to review, so I'll add a follow up PR with just this
renaming.
- Convert contract class logs to fields: Note that some classes like
`UnencryptedL2Log` still exist. This is solely for contract class logs
which have thousands of fields and so are still hashed to a single value
in the kernels/rollups/ts. In a follow up PR I'll separately convert
these to fields to benchmark the effects.
  • Loading branch information
MirandaWood authored Jan 20, 2025
1 parent 1f36a04 commit f4725d2
Show file tree
Hide file tree
Showing 123 changed files with 1,896 additions and 1,811 deletions.
2 changes: 1 addition & 1 deletion barretenberg/cpp/pil/avm/constants_gen.pil
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace constants(256);
pol MAX_NULLIFIER_READ_REQUESTS_PER_CALL = 16;
pol MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL = 16;
pol MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL = 16;
pol MAX_UNENCRYPTED_LOGS_PER_CALL = 4;
pol MAX_PUBLIC_LOGS_PER_CALL = 4;
pol MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 3000;
pol MEM_TAG_FF = 0;
pol MEM_TAG_U1 = 1;
Expand Down
78 changes: 40 additions & 38 deletions barretenberg/cpp/src/barretenberg/vm/avm/tests/execution.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1799,46 +1799,48 @@ TEST_F(AvmExecutionTests, kernelOutputEmitOpcodes)
// EXPECT_EQ(emit_nullifier_kernel_out_row->main_kernel_side_effect_out, 1);
// feed_output(emit_nullifier_out_offset, 1, 1, 0);

// TODO(#11124): Rewrite for field based public logs
// CHECK EMIT UNENCRYPTED LOG
// Unencrypted logs are hashed with sha256 and truncated to 31 bytes - and then padded back to 32 bytes
auto [contract_class_id, contract_instance] = gen_test_contract_hint(bytecode);
FF address = AvmBytecodeTraceBuilder::compute_address_from_instance(contract_instance);

std::vector<uint8_t> contract_address_bytes = address.to_buffer();
// Test log is empty, so just have to hash the contract address with 0
//
std::vector<uint8_t> bytes_to_hash;
bytes_to_hash.insert(bytes_to_hash.end(),
std::make_move_iterator(contract_address_bytes.begin()),
std::make_move_iterator(contract_address_bytes.end()));
uint32_t num_bytes = 0;
std::vector<uint8_t> log_size_bytes = to_buffer(num_bytes);
// Add the log size to the hash to bytes
bytes_to_hash.insert(bytes_to_hash.end(),
std::make_move_iterator(log_size_bytes.begin()),
std::make_move_iterator(log_size_bytes.end()));

std::array<uint8_t, 32> output = crypto::sha256(bytes_to_hash);
// Truncate the hash to 31 bytes so it will be a valid field element
FF expected_hash = FF(from_buffer<uint256_t>(output.data()) >> 8);

auto emit_log_row =
std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.main_sel_op_emit_unencrypted_log == 1; });
ASSERT_TRUE(emit_log_row != trace.end());

EXPECT_EQ(emit_log_row->main_ia, expected_hash);
// EXPECT_EQ(emit_log_row->main_side_effect_counter, 2);
// Value is 40 = 32 * log_length + 40 (and log_length is 0 in this case).
EXPECT_EQ(emit_log_row->main_ib, 40);

uint32_t emit_log_out_offset = START_EMIT_UNENCRYPTED_LOG_WRITE_OFFSET;
auto emit_log_kernel_out_row =
std::ranges::find_if(trace.begin(), trace.end(), [&](Row r) { return r.main_clk == emit_log_out_offset; });
ASSERT_TRUE(emit_log_kernel_out_row != trace.end());
EXPECT_EQ(emit_log_kernel_out_row->main_kernel_value_out, expected_hash);
EXPECT_EQ(emit_log_kernel_out_row->main_kernel_side_effect_out, 2);
EXPECT_EQ(emit_log_kernel_out_row->main_kernel_metadata_out, 40);
// feed_output(emit_log_out_offset, expected_hash, 2, 40);
// auto [contract_class_id, contract_instance] = gen_test_contract_hint(bytecode);
// FF address = AvmBytecodeTraceBuilder::compute_address_from_instance(contract_instance);

// std::vector<uint8_t> contract_address_bytes = address.to_buffer();
// // Test log is empty, so just have to hash the contract address with 0
// //
// std::vector<uint8_t> bytes_to_hash;
// bytes_to_hash.insert(bytes_to_hash.end(),
// std::make_move_iterator(contract_address_bytes.begin()),
// std::make_move_iterator(contract_address_bytes.end()));
// uint32_t num_bytes = 0;
// std::vector<uint8_t> log_size_bytes = to_buffer(num_bytes);
// // Add the log size to the hash to bytes
// bytes_to_hash.insert(bytes_to_hash.end(),
// std::make_move_iterator(log_size_bytes.begin()),
// std::make_move_iterator(log_size_bytes.end()));

// std::array<uint8_t, 32> output = crypto::sha256(bytes_to_hash);
// // Truncate the hash to 31 bytes so it will be a valid field element
// FF expected_hash = FF(from_buffer<uint256_t>(output.data()) >> 8);

// auto emit_log_row =
// std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.main_sel_op_emit_unencrypted_log == 1;
// });
// ASSERT_TRUE(emit_log_row != trace.end());

// EXPECT_EQ(emit_log_row->main_ia, expected_hash);
// // EXPECT_EQ(emit_log_row->main_side_effect_counter, 2);
// // Value is 40 = 32 * log_length + 40 (and log_length is 0 in this case).
// EXPECT_EQ(emit_log_row->main_ib, 40);

// uint32_t emit_log_out_offset = START_EMIT_UNENCRYPTED_LOG_WRITE_OFFSET;
// auto emit_log_kernel_out_row =
// std::ranges::find_if(trace.begin(), trace.end(), [&](Row r) { return r.main_clk == emit_log_out_offset; });
// ASSERT_TRUE(emit_log_kernel_out_row != trace.end());
// EXPECT_EQ(emit_log_kernel_out_row->main_kernel_value_out, expected_hash);
// EXPECT_EQ(emit_log_kernel_out_row->main_kernel_side_effect_out, 2);
// EXPECT_EQ(emit_log_kernel_out_row->main_kernel_metadata_out, 40);
// // feed_output(emit_log_out_offset, expected_hash, 2, 40);

// CHECK SEND L2 TO L1 MSG
auto send_row =
Expand Down
4 changes: 3 additions & 1 deletion barretenberg/cpp/src/barretenberg/vm/avm/trace/helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ template <typename FF_> VmPublicInputs_<FF_> convert_public_inputs(std::vector<F
ko_side_effect[dest_offset] = public_inputs_vec[pcpi_offset + 2];
}
// For EMITUNENCRYPTEDLOG
for (size_t i = 0; i < MAX_UNENCRYPTED_LOGS_PER_CALL; i++) {
for (size_t i = 0; i < MAX_PUBLIC_LOGS_PER_CALL; i++) {
// TODO(#11124): logs are now arrays of fields, we should append PUBLIC_LOG_SIZE_IN_FIELDS
// for each MAX_PUBLIC_LOGS_PER_CALL
size_t dest_offset = START_EMIT_UNENCRYPTED_LOG_WRITE_OFFSET + i;
size_t pcpi_offset =
NEW_UNENCRYPTED_LOGS_PCPI_OFFSET + (i * 3); // 3 because we have metadata, this is the window size
Expand Down
30 changes: 8 additions & 22 deletions barretenberg/cpp/src/barretenberg/vm/avm/trace/public_inputs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,30 +230,16 @@ inline void read(uint8_t const*& it, PrivateToAvmAccumulatedData& accumulated_da
read(it, accumulated_data.l2_to_l1_msgs);
}

struct LogHash {
FF value{};
uint32_t counter = 0;
FF length{};
};

inline void read(uint8_t const*& it, LogHash& log_hash)
{
using serialize::read;
read(it, log_hash.value);
read(it, log_hash.counter);
read(it, log_hash.length);
}

struct ScopedLogHash {
LogHash log_hash;
struct PublicLog {
FF contract_address{};
std::array<FF, PUBLIC_LOG_DATA_SIZE_IN_FIELDS> log{};
};

inline void read(uint8_t const*& it, ScopedLogHash& scoped_log_hash)
inline void read(uint8_t const*& it, PublicLog& public_log)
{
using serialize::read;
read(it, scoped_log_hash.log_hash);
read(it, scoped_log_hash.contract_address);
read(it, public_log.contract_address);
read(it, public_log.log);
}

struct PublicDataWrite {
Expand Down Expand Up @@ -282,9 +268,9 @@ struct AvmAccumulatedData {
*/
std::array<ScopedL2ToL1Message, MAX_L2_TO_L1_MSGS_PER_TX> l2_to_l1_msgs{};
/**
* The unencrypted logs emitted from the AVM execution.
* The public logs emitted from the AVM execution.
*/
std::array<ScopedLogHash, MAX_UNENCRYPTED_LOGS_PER_TX> unencrypted_logs_hashes{};
std::array<PublicLog, MAX_PUBLIC_LOGS_PER_TX> public_logs{};
/**
* The public data writes made in the AVM execution.
*/
Expand All @@ -298,7 +284,7 @@ inline void read(uint8_t const*& it, AvmAccumulatedData& accumulated_data)
read(it, accumulated_data.note_hashes);
read(it, accumulated_data.nullifiers);
read(it, accumulated_data.l2_to_l1_msgs);
read(it, accumulated_data.unencrypted_logs_hashes);
read(it, accumulated_data.public_logs);
read(it, accumulated_data.public_data_writes);
};

Expand Down
46 changes: 10 additions & 36 deletions barretenberg/cpp/src/barretenberg/vm/avm/trace/trace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3612,7 +3612,7 @@ AvmError AvmTraceBuilder::op_get_contract_instance(

AvmError AvmTraceBuilder::op_emit_unencrypted_log(uint8_t indirect, uint32_t log_offset, uint32_t log_size_offset)
{
std::vector<uint8_t> bytes_to_hash;
// TODO(#11124): Check this aligns with new public logs

// We keep the first encountered error
AvmError error = AvmError::NO_ERROR;
Expand All @@ -3627,12 +3627,6 @@ AvmError AvmTraceBuilder::op_emit_unencrypted_log(uint8_t indirect, uint32_t log
// This is a hack to get the contract address from the first contract instance
// Once we have 1-enqueued call and proper nested contexts, this should use that address of the current context
FF contract_address = execution_hints.all_contract_bytecode.at(0).contract_instance.address;
std::vector<uint8_t> contract_address_bytes = contract_address.to_buffer();

// Unencrypted logs are hashed with sha256 and truncated to 31 bytes - and then padded back to 32 bytes
bytes_to_hash.insert(bytes_to_hash.end(),
std::make_move_iterator(contract_address_bytes.begin()),
std::make_move_iterator(contract_address_bytes.end()));

if (is_ok(error) &&
!(check_tag(AvmMemoryTag::FF, resolved_log_offset) && check_tag(AvmMemoryTag::U32, resolved_log_size_offset))) {
Expand All @@ -3641,18 +3635,10 @@ AvmError AvmTraceBuilder::op_emit_unencrypted_log(uint8_t indirect, uint32_t log

Row row;
uint32_t log_size = 0;
uint32_t num_bytes = 0;

if (is_ok(error)) {
log_size = static_cast<uint32_t>(unconstrained_read_from_memory(resolved_log_size_offset));

// The size is in fields of 32 bytes, the length used for the hash is in terms of bytes
num_bytes = log_size * 32;
std::vector<uint8_t> log_size_bytes = to_buffer(num_bytes);
// Add the log size to the hash to bytes
bytes_to_hash.insert(bytes_to_hash.end(),
std::make_move_iterator(log_size_bytes.begin()),
std::make_move_iterator(log_size_bytes.end()));
ASSERT(log_size <= PUBLIC_LOG_DATA_SIZE_IN_FIELDS);

if (!check_slice_mem_range(resolved_log_offset, log_size)) {
error = AvmError::MEM_SLICE_OUT_OF_RANGE;
Expand All @@ -3666,7 +3652,7 @@ AvmError AvmTraceBuilder::op_emit_unencrypted_log(uint8_t indirect, uint32_t log
// Can't return earlier as we do elsewhere for side-effect-limit because we need
// to at least retrieve log_size first to charge proper gas.
// This means a tag error could occur before side-effect-limit first.
if (is_ok(error) && unencrypted_log_write_counter >= MAX_UNENCRYPTED_LOGS_PER_TX) {
if (is_ok(error) && unencrypted_log_write_counter >= MAX_PUBLIC_LOGS_PER_TX) {
error = AvmError::SIDE_EFFECT_LIMIT_REACHED;
auto row = Row{
.main_clk = clk,
Expand All @@ -3681,39 +3667,27 @@ AvmError AvmTraceBuilder::op_emit_unencrypted_log(uint8_t indirect, uint32_t log
return error;
}
unencrypted_log_write_counter++;
std::vector<FF> log_values{ contract_address };

if (is_ok(error)) {
// We need to read the rest of the log_size number of elements
for (uint32_t i = 0; i < log_size; i++) {
FF log_value = unconstrained_read_from_memory(resolved_log_offset + i);
std::vector<uint8_t> log_value_byte = log_value.to_buffer();
bytes_to_hash.insert(bytes_to_hash.end(),
std::make_move_iterator(log_value_byte.begin()),
std::make_move_iterator(log_value_byte.end()));
log_values.emplace_back(log_value);
}

std::array<uint8_t, 32> output = crypto::sha256(bytes_to_hash);
// Truncate the hash to 31 bytes so it will be a valid field element
FF trunc_hash = FF(from_buffer<uint256_t>(output.data()) >> 8);

// The + 32 here is for the contract_address in bytes, the +4 is for the extra 4 bytes that contain log_size
// and is prefixed to message see toBuffer in unencrypted_l2_log.ts
FF length_of_preimage = num_bytes + 32 + 4;
// The + 4 is because the kernels store the length of the
// processed log as 4 bytes; thus for this length value to match the log length stored in the kernels, we
// need to add four to the length here. [Copied from unencrypted_l2_log.ts]
FF metadata_log_length = length_of_preimage + 4;
// Add 1 for the contract address (= PUBLIC_LOG_SIZE_IN_FIELDS)
log_values.resize(PUBLIC_LOG_DATA_SIZE_IN_FIELDS + 1, FF::zero());
row = Row{
.main_clk = clk,
.main_call_ptr = call_ptr,
.main_ia = trunc_hash,
.main_ib = metadata_log_length,
// .main_ia = trunc_hash,
// .main_ib = metadata_log_length,
.main_internal_return_ptr = internal_return_ptr,
.main_pc = pc,
.main_sel_op_emit_unencrypted_log = FF(1),
};
// Write to offset
// kernel_trace_builder.op_emit_unencrypted_log(clk, side_effect_counter, trunc_hash, metadata_log_length);
row.main_sel_op_emit_unencrypted_log = FF(1);
} else {
row = Row{
.main_clk = clk,
Expand Down
13 changes: 7 additions & 6 deletions barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
#define MAX_NULLIFIER_READ_REQUESTS_PER_CALL 16
#define MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL 16
#define MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL 16
#define MAX_UNENCRYPTED_LOGS_PER_CALL 4
#define MAX_PUBLIC_LOGS_PER_CALL 4
#define MAX_NOTE_HASHES_PER_TX 64
#define MAX_NULLIFIERS_PER_TX 64
#define MAX_ENQUEUED_CALLS_PER_TX 32
#define MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX 63
#define MAX_L2_TO_L1_MSGS_PER_TX 8
#define MAX_UNENCRYPTED_LOGS_PER_TX 8
#define MAX_PUBLIC_LOGS_PER_TX 8
#define PUBLIC_LOG_DATA_SIZE_IN_FIELDS 13
#define MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS 3000
#define CANONICAL_AUTH_REGISTRY_ADDRESS 1
#define DEPLOYER_CONTRACT_ADDRESS 2
Expand All @@ -44,14 +45,14 @@
#define PUBLIC_INNER_CALL_REQUEST_LENGTH 13
#define STATE_REFERENCE_LENGTH 8
#define TOTAL_FEES_LENGTH 1
#define PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH 864
#define AVM_ACCUMULATED_DATA_LENGTH 320
#define AVM_CIRCUIT_PUBLIC_INPUTS_LENGTH 1011
#define PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH 908
#define AVM_ACCUMULATED_DATA_LENGTH 400
#define AVM_CIRCUIT_PUBLIC_INPUTS_LENGTH 1091
#define AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS 86
#define MAX_PUBLIC_CALLS_TO_UNIQUE_CONTRACT_CLASS_IDS 21
#define AVM_PROOF_LENGTH_IN_FIELDS 4154
#define AVM_PUBLIC_COLUMN_MAX_SIZE 1024
#define AVM_PUBLIC_INPUTS_FLATTENED_SIZE 2912
#define AVM_PUBLIC_INPUTS_FLATTENED_SIZE 2956
#define MEM_TAG_FF 0
#define MEM_TAG_U1 1
#define MEM_TAG_U8 2
Expand Down
3 changes: 2 additions & 1 deletion barretenberg/cpp/src/barretenberg/vm/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ inline const std::size_t KERNEL_OUTPUTS_LENGTH =
MAX_NOTE_HASH_READ_REQUESTS_PER_CALL + MAX_NULLIFIER_READ_REQUESTS_PER_CALL +
MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL + MAX_L1_TO_L2_MSG_READ_REQUESTS_PER_CALL +
MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL + MAX_PUBLIC_DATA_READS_PER_CALL + MAX_NOTE_HASHES_PER_CALL +
MAX_NULLIFIERS_PER_CALL + MAX_L2_TO_L1_MSGS_PER_CALL + MAX_UNENCRYPTED_LOGS_PER_CALL;
MAX_NULLIFIERS_PER_CALL + MAX_L2_TO_L1_MSGS_PER_CALL + MAX_PUBLIC_LOGS_PER_CALL;

static_assert(KERNEL_INPUTS_LENGTH < AVM_PUBLIC_COLUMN_MAX_SIZE,
"The kernel inputs length cannot exceed the max size of a public column. This is a requirement for the "
Expand Down Expand Up @@ -87,6 +87,7 @@ inline const uint32_t START_SIDE_EFFECT_COUNTER_PCPI_OFFSET =
// START_SIDE_EFFECT_COUNTER
// END_SIDE_EFFECT_COUNTER -> + 1
// NEW_UNENCRYPTED_LOGS -> + 2
// TODO(#11124): Edit this in line with public logs?
inline const uint32_t NEW_UNENCRYPTED_LOGS_PCPI_OFFSET = START_SIDE_EFFECT_COUNTER_PCPI_OFFSET + 2;

// END INDEXES in the PUBLIC_CIRCUIT_PUBLIC_INPUTS
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ To emit unencrypted logs you don't need to import any library. You call the cont

Once emitted, unencrypted events are stored in AztecNode and can be queried by anyone:

#include_code get_logs /yarn-project/end-to-end/src/fixtures/utils.ts typescript
#include_code get_logs /yarn-project/end-to-end/src/e2e_ordering.test.ts typescript

## Costs

Expand Down
27 changes: 26 additions & 1 deletion docs/docs/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,32 @@ keywords: [sandbox, aztec, notes, migration, updating, upgrading]

Aztec is in full-speed development. Literally every version breaks compatibility with the previous ones. This page attempts to target errors and difficulties you might encounter when upgrading, and how to resolve them.

## TBD
## 0.72.0
### Public logs replace unencrypted logs
Any log emitted from public is now known as a public log, rather than an unencrypted log. This means methods relating to these logs have been renamed e.g. in the pxe, archiver, txe:
```diff
- getUnencryptedLogs(filter: LogFilter): Promise<GetUnencryptedLogsResponse>
- getUnencryptedEvents<T>(eventMetadata: EventMetadataDefinition, from: number, limit: number): Promise<T[]>
+ getPublicLogs(filter: LogFilter): Promise<GetPublicLogsResponse>
+ getPublicEvents<T>(eventMetadata: EventMetadataDefinition, from: number, limit: number): Promise<T[]>
```

These logs were treated as bytes in the node and as hashes in the protocol circuits. Now, public logs are treated as fields everywhere:
```diff
- unencryptedLogs: UnencryptedTxL2Logs
- unencrypted_logs_hashes: [ScopedLogHash; MAX_UNENCRYPTED_LOGS_PER_TX]
+ publicLogs: PublicLog[]
+ public_logs: [PublicLog; MAX_PUBLIC_LOGS_PER_TX]
```
A `PublicLog` contains the log (as an array of fields) and the app address.

This PR also renamed encrypted events to private events:
```diff
- getEncryptedEvents<T>(eventMetadata: EventMetadataDefinition, from: number, limit: number, vpks: Point[]): Promise<T[]>
+ getPrivateEvents<T>(eventMetadata: EventMetadataDefinition, from: number, limit: number, vpks: Point[]): Promise<T[]>
```

## 0.70.0
### [Aztec.nr] Removal of `getSiblingPath` oracle
Use `getMembershipWitness` oracle instead that returns both the sibling path and index.

Expand Down
Loading

1 comment on commit f4725d2

@AztecBot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'C++ Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.05.

Benchmark suite Current: f4725d2 Previous: 9e5ea3a Ratio
commit(t) 3557581850 ns/iter 3104952680 ns/iter 1.15
Goblin::merge(t) 146190272 ns/iter 135468036 ns/iter 1.08

This comment was automatically generated by workflow using github-action-benchmark.

CC: @ludamad @codygunton

Please sign in to comment.