Skip to content

Conversation

@Fumuran
Copy link
Contributor

@Fumuran Fumuran commented Dec 18, 2025

In this PR we primarily do two things:

  • Create a new agglayer::collections module
  • Create a new keccak-based MMR frontier for this module

WiP

TODO:

  • White module description inside the file
  • White the proper docs for this collection
  • Write tests

Closes: #2105

@Fumuran Fumuran force-pushed the andrew-keccak-mmr-frontier branch from f83c1bd to 107d4a2 Compare December 19, 2025 16:48
@bobbinth
Copy link
Contributor

Without having looked at the code, I would also probably add implementation of this data structure in Rust. For now, it would probably be purely for testing purposes.

Base automatically changed from andrew-migrate-to-v20-vm to next December 20, 2025 07:51
Copy link
Contributor

@mmagician mmagician left a comment

Choose a reason for hiding this comment

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

Really great work! The algorithm is correct AFAICS

I suppose you're using a small tree height =5 to showcase and test the functionality. For a height of 32, we're going to end up with a lot of boilerplate, I wonder what's the cleanest way to load all the canonical zeros to memory.

# => [zero_array_ptr, mmr_root_ptr]

# store the zero for the height 0
push.1676014350.378744630.4127735880.2512168523.3362732168.2839872470.2825030484.3656125737
Copy link
Contributor

Choose a reason for hiding this comment

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

We should hardcode those constants at the top of the file

Comment on lines +21 to +30
# The offset of the MMR root.
#
# Q: do we need to store the root? It seems like we never return it
const MMR_ROOT_OFFSET = 4

# The offset of the array of the zero hashes of respective heights.
const ZEROS_OFFSET = 12 # 6 double words, 48 felts in total

# The offset of the array of the frontier nodes of respective heights.
const FRONTIER_OFFSET = 60 # 6 double words, 48 felts in total
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be great to add a description of the expected memory layout, like so:

[num_leaves, root, frontier_L[0], frontier_R[0], frontier_L[1], frontier_R[1], ... ]

Copy link
Contributor

Choose a reason for hiding this comment

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

I would probably store the zeros separately from the above expected layout.
The reason is that this layout is expected to be provided by the caller of this procedure, whereas the zeros are internal, and not provided by the user.

So in practical terms, the canonical zeros could be stored at the very end.

while.true
# => [iter_counter, num_leaves, mmr_frontier_ptr]

# get the pointer to the frontier node of the current height
Copy link
Contributor

Choose a reason for hiding this comment

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

what is the initial state of the frontier array? Is it zeros, or canonical zeros at tree height? Or something else?


# load the current hash from the local memory back to the stack
padw loc_loadw.4 padw loc_loadw.0 swapdw
# => [FRONTIER[iter_counter]_LO, FRONTIER[iter_counter]_HI, CUR_HASH_LO, CUR_HASH_HI, iter_counter, num_leaves, mmr_frontier_ptr]
Copy link
Contributor

Choose a reason for hiding this comment

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

Following the algorithm description here, CUR_HASH has the value of NEW_LEAF in the first iteration - let's make this explicit in the inline comments, because it's not clear w/o knowing the algorithm in detail where CUR_HASH suddenly appears from

Comment on lines +105 to +107
# store the new leaf to the local memory
loc_storew.0 dropw
loc_storew.4 dropw
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
# store the new leaf to the local memory
loc_storew.0 dropw
loc_storew.4 dropw
# set CUR_HASH = NEW_LEAF and store to local memory
loc_storew.0 dropw
loc_storew.4 dropw

(see a comment below about where CUR_HASH comes from

# update the counter
push.1 add

# update the `num_leaves` (shift it right by 1 bit)
Copy link
Contributor

Choose a reason for hiding this comment

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

I would avoid using num_leaves as a mutable variable (the actual number of leaves doesn't change throughout the algorithm), and instead follow the convention to set:

let mut idx = num_leaves;

at the beginning of the procedure, and just use idx later

exec.mem_store_double_word movup.8 drop
# => [FIN_HASH_LO, FIN_HASH_HI, frontier[tree_height]_ptr]

# store the final hash to the frontier[tree_height]_ptr (frontier[5])
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't this step already happen as part of the loop (for the case when the last bit is zero)?

Comment on lines +23 to +24
# Q: do we need to store the root? It seems like we never return it
const MMR_ROOT_OFFSET = 4
Copy link
Contributor

Choose a reason for hiding this comment

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

I suppose we don't have to store it as part of the append_and_update_frontier procedure. It will be the caller's responsibility to retrieve the result of append_and_update_frontier and update that root in its account storage.

Comment on lines +239 to +247
#! Inputs: [WORD_1, WORD_2, ptr]
#! Outputs: [WORD_1, WORD_2, ptr]
proc mem_store_double_word
dup.8 mem_storew_be swapw
# => [WORD_2, WORD_1, ptr]

dup.8 add.4 mem_storew_be swapw
# => [WORD_1, WORD_2, ptr]
end
Copy link
Contributor

Choose a reason for hiding this comment

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

a future note: these might be useful across the board, let's leave as-is for now but keep in mind that we might want to place these helpers in a more accessible location for other procedures to make use of.

@Fumuran
Copy link
Contributor Author

Fumuran commented Jan 8, 2026

Superseded by #2245

@Fumuran Fumuran closed this Jan 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Keccak-based MMR frontier (agglayer::collections::mmr_frontier_keccak)

4 participants