Skip to content

Commit 7cf39cb

Browse files
author
AztecBot
committed
Merge branch 'next' into ad/chore/ci-release-pr-canary
2 parents 4ad4849 + 81e43e8 commit 7cf39cb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1173
-566
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,5 @@ docs/.yarn/install-state.gz
3131
docs/docs/protocol-specs/public-vm/gen/
3232

3333
__pycache__
34+
35+
*.local.md

.vscode/settings.json

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,15 +197,27 @@
197197
"bb-pilcom/Cargo.toml"
198198
],
199199
"files.watcherExclude": {
200+
"**/.docusaurus/**": true,
200201
"**/.git/**": true,
201-
"**/node_modules/**": true,
202202
"**/.hg/store/**": true,
203-
"**/target/**": true,
204-
"**/l1-contracts/lib/**": true,
203+
"**/.yarn/**": true,
205204
"**/barretenberg/cpp/build*/**": true,
206205
"**/barretenberg/cpp/src/wasi-sdk*/**": true,
206+
"**/barretenberg/docs/build/**": true,
207+
"**/barretenberg/docs/static/**": true,
208+
"**/barretenberg/docs/versioned_docs/**": true,
207209
"**/dest/**": true,
208-
"**/noir/noir-repo/docs/versioned_docs/**": true
210+
"**/docs/.docusaurus/**": true,
211+
"**/docs/build/**": true,
212+
"**/docs/developer_versioned_docs/**": true,
213+
"**/docs/network_versioned_docs/**": true,
214+
"**/docs/processed-docs-cache/**": true,
215+
"**/docs/processed-docs/**": true,
216+
"**/docs/static/**": true,
217+
"**/l1-contracts/lib/**": true,
218+
"**/node_modules/**": true,
219+
"**/noir/noir-repo/docs/versioned_docs/**": true,
220+
"**/target/**": true
209221
},
210222
"files.trimTrailingWhitespace": true,
211223
"files.insertFinalNewline": true,

ci3/grind_test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ function build {
4141

4242
function grind {
4343
echo_header "grinding for $timeout"
44-
local par_cmd="CI=0 TRACK_TEST_FAIL=1 run_test_cmd '${hash_prefix}:NAME=test-{} ${test_cmd}'"
44+
# NAME_POSTFIX allows for grinding compose tests in parallel.
45+
local par_cmd="NAME_POSTFIX={} CI=0 TRACK_TEST_FAIL=1 run_test_cmd '${hash_prefix}:NAME=test-{} ${test_cmd}'"
4546
local joblog=$(mktemp)
4647

4748
# Run for full timeout, logging all results (no --halt, collect all failure data)

docs/netlify.toml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@
340340
# (Netlify processes redirects top-to-bottom, first match wins)
341341
[[redirects]]
342342
from = "/the_aztec_network/guides/run_nodes/how_to_run_sequencer"
343-
to = "/operate/operators/setup/sequencer_management"
343+
to = "/operate/operators/sequencer-management"
344344

345345
[[redirects]]
346346
from = "/the_aztec_network/guides/run_nodes/how_to_run_prover"
@@ -477,6 +477,25 @@
477477
to = "/operate/operators/prerequisites"
478478
status = 301
479479

480+
# =============================================================================
481+
# ACTIVE REDIRECTS: Underscore to Hyphen Folder Normalization
482+
# Folder names use hyphens but old links may use underscores
483+
# =============================================================================
484+
[[redirects]]
485+
from = "/operate/operators/sequencer_management/*"
486+
to = "/operate/operators/sequencer-management/:splat"
487+
status = 301
488+
489+
[[redirects]]
490+
from = "/operate/testnet/operators/sequencer_management/*"
491+
to = "/operate/testnet/operators/sequencer-management/:splat"
492+
status = 301
493+
494+
[[redirects]]
495+
from = "/operate/ignition/operators/sequencer_management/*"
496+
to = "/operate/ignition/operators/sequencer-management/:splat"
497+
status = 301
498+
480499
# =============================================================================
481500
# ACTIVE REDIRECTS: devnet.5 → devnet.20251212 Structure Migration
482501
# These redirects handle the URL structure changes between versions.

noir-projects/noir-protocol-circuits/crates/rollup-lib/src/checkpoint_merge/utils/merge_checkpoint_rollups.nr

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::abis::CheckpointRollupPublicInputs;
2-
use types::{constants::MAX_CHECKPOINTS_PER_EPOCH, utils::arrays::copy_items_into_array};
2+
use types::{constants::MAX_CHECKPOINTS_PER_EPOCH, utils::arrays::splice_at_count};
33

44
pub fn merge_checkpoint_rollups(
55
left: CheckpointRollupPublicInputs,
@@ -8,11 +8,8 @@ pub fn merge_checkpoint_rollups(
88
let num_left_checkpoints = left.num_checkpoints() as u32;
99
let num_right_checkpoints = right.num_checkpoints() as u32;
1010

11-
// Make sure that the total number of checkpoints does not exceed the maximum allowed in an epoch, to prevent the
12-
// merged arrays (`checkpoint_header_hashes`, `out_hashes`, `fees`) from being truncated within the below calls to
13-
// `copy_items_into_array`.
11+
// Make sure that the total number of checkpoints does not exceed the maximum allowed in an epoch.
1412
let num_checkpoints = num_left_checkpoints + num_right_checkpoints;
15-
1613
assert(
1714
num_checkpoints <= MAX_CHECKPOINTS_PER_EPOCH,
1815
"total number of checkpoints exceeds max allowed in an epoch",
@@ -24,27 +21,32 @@ pub fn merge_checkpoint_rollups(
2421
// (which is the only property that doesn't have empty values), for the number of fees.
2522
// Hence, we don't need to assert left.num_fees == num_left_checkpoints and right.num_fees == num_right_checkpoints.
2623

27-
// We use `copy_items_into_array` here because we know exactly how many items should be taken from each side when
28-
// merging checkpoints.
29-
// It is especially critical for `fees` to copy the exact amount of items from both checkpoints:
30-
// The L1 contracts will process fees by index, assuming each entry corresponds to the checkpoint at the same
31-
// position. However, a checkpoint may have zero fees and recipient as it is not prohibited by the circuit.
32-
// Therefore, preserving empty values is important.
33-
// Using this helper (instead of helper like `array_merge`) ensures those empty entries are copied over.
24+
// `splice_at_count` uses the number of **left** checkpoints to decide exactly how many items to include from each
25+
// side. This approach is more efficient than merging all non-empty items, and, importantly, it ensures the correct
26+
// alignment of array items. It would be incorrect to simply merge non-empty items.
27+
// For example, while header hashes are always present, the `fees` array can have empty (zeroed) entries that must
28+
// be preserved at their original indices. The L1 contract expects the fee at each index to correspond to the
29+
// checkpoint at the same index, even if that fee (or recipient) is zero. Therefore, we must always preserve all fee
30+
// items during the merge. Merging by `num_checkpoints` guarantees that array indices in all merged arrays remain
31+
// properly aligned.
3432

35-
let checkpoint_header_hashes = copy_items_into_array(
33+
let checkpoint_header_hashes = splice_at_count(
3634
left.checkpoint_header_hashes,
3735
num_left_checkpoints,
3836
right.checkpoint_header_hashes,
39-
num_right_checkpoints,
37+
);
38+
// Sanity check that the length of the array is defined correctly.
39+
std::static_assert(
40+
checkpoint_header_hashes.len() == MAX_CHECKPOINTS_PER_EPOCH,
41+
"The length of the checkpoint header hashes array does not match the constant MAX_CHECKPOINTS_PER_EPOCH",
4042
);
4143

4244
// We can't merge fees for the same recipient since the l1 contract iterates per checkpoint.
43-
let fees = copy_items_into_array(
44-
left.fees,
45-
num_left_checkpoints,
46-
right.fees,
47-
num_right_checkpoints,
45+
let fees = splice_at_count(left.fees, num_left_checkpoints, right.fees);
46+
// Sanity check that the length of the array is defined correctly.
47+
std::static_assert(
48+
fees.len() == MAX_CHECKPOINTS_PER_EPOCH,
49+
"The length of the fees array does not match the constant MAX_CHECKPOINTS_PER_EPOCH",
4850
);
4951

5052
CheckpointRollupPublicInputs {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use types::{
2+
abis::{log_hash::LogHash, private_log::PrivateLog},
3+
side_effect::Scoped,
4+
utils::arrays::array_length_until,
5+
};
6+
7+
// The logic in this file relies on the property that log arrays are "dense-trimmed": all logs with non-zero length
8+
// appear first, followed by logs with zero length (used as padding).
9+
// This structure is enforced by the Tail-to-Public kernel circuit and remains intact even after array concatenation in
10+
// the public_tx_effect_builder. As a result, we can safely use the index of the first zero-length log to determine the
11+
// logical length of the logs array.
12+
//
13+
// @dev: Unlike other smaller arrays (note hashes, nullifiers, and l2-to-l1 messages) that use `array_length`, it is
14+
// more efficient for log arrays to use `array_length_until` and iterate through all lengths (u32).
15+
// `array_length_until` is also more efficient than an optimized version of `array_length` that would call the
16+
// unconstrained `find_first_index` and then validate the result by checking just the lengths of two adjacent elements
17+
// (rather than verifying whether the entire log is empty).
18+
19+
/// Returns the number of private logs with length > 0.
20+
pub fn get_private_log_array_length<let N: u32>(array: [PrivateLog; N]) -> u32 {
21+
array_length_until(array, |log| log.length == 0)
22+
}
23+
24+
/// Returns the number of contract class log hashes with length > 0.
25+
pub fn get_contract_class_log_hash_array_length<let N: u32>(array: [Scoped<LogHash>; N]) -> u32 {
26+
array_length_until(array, |log| log.inner.length == 0)
27+
}

noir-projects/noir-protocol-circuits/crates/rollup-lib/src/tx_base/components/mod.nr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod fees;
2+
pub mod log_array_length;
23
pub mod private_tail_validator;
34
pub mod private_tx_effect_builder;
45
pub mod public_tx_effect_builder;

noir-projects/noir-protocol-circuits/crates/rollup-lib/src/tx_base/components/private_tail_validator.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub fn validate_tx_constant_data(
6565
},
6666
block_last_archive_root,
6767
),
68-
"Membership check failed: previous block hash not found in archive tree",
68+
"Membership check failed: anchor block header hash not found in archive tree",
6969
);
7070

7171
assert_eq(tx_chain_id, block_chain_id, "Mismatched chain_id between kernel and rollup");

noir-projects/noir-protocol-circuits/crates/rollup-lib/src/tx_base/components/private_tx_effect_builder.nr

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
use crate::tx_base::components::fees::compute_transaction_fee;
2+
use super::log_array_length::{
3+
get_contract_class_log_hash_array_length, get_private_log_array_length,
4+
};
25
use types::{
36
abis::{
47
block_constant_data::BlockConstantData, contract_class_log::ContractClassLog,
@@ -13,7 +16,7 @@ use types::{
1316
data::public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage,
1417
hash::compute_l2_to_l1_message_hash,
1518
traits::{Empty, Hash},
16-
utils::arrays::{array_length, array_length_until},
19+
utils::arrays::array_length,
1720
};
1821

1922
pub fn build_tx_effect(
@@ -80,23 +83,23 @@ pub fn build_tx_effect(
8083
contract_class_logs,
8184
};
8285

83-
// We can use `array_length_until` to check only the length, because the kernel circuits guarantee that logs with
84-
// non-zero length are left-packed. In other words, there cannot be a log with length 0 before a log with non-zero
85-
// length.
86-
let private_logs_array_length =
87-
array_length_until(accumulated_data.private_logs, |log| log.length == 0);
88-
let contract_class_logs_array_length = array_length_until(
89-
accumulated_data.contract_class_logs_hashes,
90-
|log| log.inner.length == 0,
91-
);
86+
// The Tail kernel circuit guarantees that all accumulated data arrays are "dense-trimmed": all actual data emitted
87+
// by the tx are non-empty and appear first, followed by empty padding elements. This means it is safe to use
88+
// `array_length` to get the true logical length of these arrays.
89+
// For log arrays, we use specialized functions (get_private_log_array_length,
90+
// get_contract_class_log_hash_array_length) that efficiently determine the length by finding the first zero-length
91+
// log. Because the kernel enforces that only actual logs have nonzero length and all padding logs have zero length,
92+
// these functions safely provide the correct count, independent of the log contents themselves.
9293

9394
let tx_effect_array_lengths = TxEffectArrayLengths {
9495
note_hashes: array_length(accumulated_data.note_hashes),
9596
nullifiers: array_length(accumulated_data.nullifiers),
9697
l2_to_l1_msgs: array_length(l2_to_l1_msgs),
9798
public_data_writes: 1, // No public functions were called. This is the fee_payer's FeeJuice balance decrementation.
98-
private_logs: private_logs_array_length,
99-
contract_class_logs: contract_class_logs_array_length,
99+
private_logs: get_private_log_array_length(accumulated_data.private_logs),
100+
contract_class_logs: get_contract_class_log_hash_array_length(
101+
accumulated_data.contract_class_logs_hashes,
102+
),
100103
};
101104

102105
(tx_effect, tx_effect_array_lengths)

noir-projects/noir-protocol-circuits/crates/rollup-lib/src/tx_base/components/public_tx_effect_builder.nr

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
use crate::tx_base::components::private_tail_validator;
2+
use super::log_array_length::{
3+
get_contract_class_log_hash_array_length, get_private_log_array_length,
4+
};
25
use types::{
36
abis::{
47
avm_circuit_public_inputs::AvmCircuitPublicInputs, contract_class_log::ContractClassLog,
@@ -8,7 +11,7 @@ use types::{
811
constants::{CONTRACT_CLASS_LOG_SIZE_IN_FIELDS, MAX_CONTRACT_CLASS_LOGS_PER_TX},
912
hash::compute_l2_to_l1_message_hash,
1013
traits::Hash,
11-
utils::arrays::{array_length_until, array_merge},
14+
utils::arrays::splice_at_count,
1215
};
1316

1417
pub fn build_tx_effect(
@@ -49,13 +52,25 @@ pub fn build_tx_effect(
4952
// are conditionally discarded by the avm circuit. Private logs and contract class logs are
5053
// different, in that they're only emitted by private functions and so the avm doesn't care about them
5154
// or "see" them.
55+
//
56+
// Note: splice_at_count is safe to use here because both input arrays were originally split from a single
57+
// dense-trimmed array [T; MAX_PER_TX] in the Tail-to-Public kernel circuit.
58+
// (For general arrays, dense-trimmed means all non-empty entries appear before any empty entries; for logs, all
59+
// logs with nonzero length appear before any zero-length logs.)
60+
// The Tail-to-Public kernel ensures the 2 split arrays are also dense-trimmed.
61+
// This guarantees that merging them will never produce more than MAX_PER_TX items and no actual values will be lost
62+
// or truncated. And after concatenation, the resulting array remains dense-trimmed.
5263

5364
// Conditionally discard the revertible private logs.
5465
let private_logs = if reverted {
5566
private_to_public.non_revertible_accumulated_data.private_logs
5667
} else {
57-
array_merge(
68+
let num_non_revertible_private_logs = get_private_log_array_length(
5869
private_to_public.non_revertible_accumulated_data.private_logs,
70+
);
71+
splice_at_count(
72+
private_to_public.non_revertible_accumulated_data.private_logs,
73+
num_non_revertible_private_logs,
5974
private_to_public.revertible_accumulated_data.private_logs,
6075
)
6176
};
@@ -64,8 +79,12 @@ pub fn build_tx_effect(
6479
let contract_class_log_hashes = if reverted {
6580
private_to_public.non_revertible_accumulated_data.contract_class_logs_hashes
6681
} else {
67-
array_merge(
82+
let num_non_revertible_contract_class_log_hashes = get_contract_class_log_hash_array_length(
83+
private_to_public.non_revertible_accumulated_data.contract_class_logs_hashes,
84+
);
85+
splice_at_count(
6886
private_to_public.non_revertible_accumulated_data.contract_class_logs_hashes,
87+
num_non_revertible_contract_class_log_hashes,
6988
private_to_public.revertible_accumulated_data.contract_class_logs_hashes,
7089
)
7190
};
@@ -102,12 +121,9 @@ pub fn build_tx_effect(
102121
contract_class_logs,
103122
};
104123

105-
// We can use `array_length_until` to check only the length, because the kernel circuits guarantee that logs with
106-
// non-zero length are left-packed. In other words, there cannot be a log with length 0 before a log with non-zero
107-
// length.
108-
let private_logs_array_length = array_length_until(private_logs, |log| log.length == 0);
124+
let private_logs_array_length = get_private_log_array_length(private_logs);
109125
let contract_class_logs_array_length =
110-
array_length_until(contract_class_log_hashes, |log| log.inner.length == 0);
126+
get_contract_class_log_hash_array_length(contract_class_log_hashes);
111127

112128
let tx_effect_array_lengths = TxEffectArrayLengths {
113129
note_hashes: avm.accumulated_data_array_lengths.note_hashes,

0 commit comments

Comments
 (0)