Skip to content

WIP: prototype MMB - #3234

Draft
krushimir wants to merge 9 commits into
0xMiden:nextfrom
reilabs:krushimir/mmb
Draft

WIP: prototype MMB#3234
krushimir wants to merge 9 commits into
0xMiden:nextfrom
reilabs:krushimir/mmb

Conversation

@krushimir

Copy link
Copy Markdown
Contributor

Note

Draft/WIP, opening for visibility. VM-side companion to 0xMiden/crypto#1028.

Adds a VM-side Mountain Merkle Belt prototype for research/benchmarking.

@krushimir krushimir changed the title WIP: prototype MMB root and proof verification in VM WIP: prototype MMB Jun 5, 2026
# Load and check position < num_leaves.
dup add.1 mem_load
# => [position, proof_ptr, ROOT, num_leaves, ...]
dup.6 u32assert2 u32lt assert.err="position is not an existing MMB leaf"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this only range-checks position, then drops it before building the root. That means the proof is not tied to the claimed leaf index. I could relabel a valid proof for position 0 as position 1 and it would still verify:

let mmb = TestMmb::from_len(17);
let mut proof = mmb.open(0);
proof.position = 1;

build_test!(&source)
    .execute()
    .expect_err("tampered position must not authenticate the proof");

That test fails because execution succeeds. If callers use the opened position for recency or membership claims, this accepts the right leaf/root with the wrong position.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed! The verifier no longer trusts the directions stored in the proof. Instead it computes the expected path from the authenticated (num_leaves, position) pair (same rules as belt/shape.rs in crypto) and checks the proof length and every direction against it.

Your scenario is now a negative test (test_mmb_verify_leaf_rejects_tampered_position), along with tests for tampered directions and for the shape math itself.

Comment thread crates/lib/core/benches/common.rs Outdated

pub fn compile(core_lib: &CoreLibrary, source: &str) -> Program {
Assembler::default()
.with_static_library(core_lib.library())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We've recently changed those APIs unfrotunately.

The live APIs look like Assembler::with_package(..., Linkage::...) and CoreLibrary::package(), so the new bench targets make the core-lib feature checks fail before they run.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Rebased onto next and migrated!

@krushimir

Copy link
Copy Markdown
Contributor Author

Some benchmarks. Numbers are execution-trace rows on minimal standalone programs (padded = proving cost). Verify shows padded cost since a verify-only program is the real light-client workload; the other tables are MMR-vs-MMB comparisons, not absolute costs.

Verifymmb_verify_leaf, identical traces for any n from 2^10 to 2^26:

recency k proof len rows (max / padded)
1 3 1,144 / 2,048
256 16 1,922 / 2,048
4,096 24 2,589 / 4,096
2^20 40 3,915 / 4,096
2^26 52 4,911 / 8,192

Unpack and root (rows; MMR has popcount-many peaks, so power-of-two sizes flatter it — 2^31−1 is the like-for-like point with 31 peaks on both sides):

leaves unpack_frontier mmb_unpack mmr::root mmb_root
2^16 1,558 1,693 1,348 1,654
2^20 1,702 2,037 1,492 2,001
2^26 1,902 2,556 1,692 2,518
2^31−1 3,752 2,999 3,727 2,950

Append (rows):

case mmr::add mmb_add
no merge 431 336
common 525 715
worst 1,077 (16 merges) 1,241 (deep slide)

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.

2 participants