Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BlockWitness: pass inputs to the VM through the advice provider #516

Merged
merged 2 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
- Optimized state synchronizations by removing unnecessary fetching and parsing of note details (#462).
- [BREAKING] Changed `GetAccountDetailsResponse` field to `details` (#481).
- Improve `--version` by adding build metadata (#495).
- [BREAKING] Introduced additional limits for note/account number (#503).
- [BREAKING] Introduced additional limits for note/account number (#503).
- [BREAKING] Removed support for basic wallets in genesis creation (#510).
- Added `GetAccountProofs` endpoint (#506).
- Migrated faucet from actix-web to axum (#511).
- Changed the `BlockWitness` to pass the inputs to the VM using only advice provider (#516).

## 0.5.1 (2024-09-12)

Expand Down
224 changes: 149 additions & 75 deletions crates/block-producer/src/block_builder/prover/asm/block_kernel.masm
Original file line number Diff line number Diff line change
Expand Up @@ -13,154 +13,228 @@ const.CHAIN_MMR_PTR=1000

#! Compute the account root
#!
#! Stack: [num_accounts_updated, OLD_ACCOUNT_ROOT,
#! NEW_ACCOUNT_HASH_0, account_id_0, ... , NEW_ACCOUNT_HASH_n, account_id_n]
#! Output: [NEW_ACCOUNT_ROOT]
#! Inputs:
#! Operand stack: []
#! Advice stack: [num_accounts_updated, OLD_ACCOUNT_ROOT, [NEW_ACCOUNT_HASH_i, account_id_i]]
#! Outputs:
#! Operand stack: [NEW_ACCOUNT_ROOT]
proc.compute_account_root
dup neq.0
# => [0 or 1, num_accounts_updated, OLD_ACCOUNT_ROOT,
# NEW_ACCOUNT_HASH_0, account_id_0, ... , NEW_ACCOUNT_HASH_n, account_id_n]
# move the number of updated accounts and an old account root to the operand stack
adv_push.5
# OS => [OLD_ACCOUNT_ROOT, num_accounts_updated]
# AS => [[NEW_ACCOUNT_HASH_i, account_id_i]]

# assess if we should loop
dup.4 neq.0
# OS => [flag, OLD_ACCOUNT_ROOT, num_accounts_updated]
# AS => [[NEW_ACCOUNT_HASH_i, account_id_i]]

while.true
# stack: [counter, ROOT_0, ..., NEW_ACCOUNT_HASH_i, account_id_i , ...]
# num_accounts_updated here serves as a counter, so rename it accordingly
# old account root will be updated in each iteration, so rename it to the ROOT_i
# OS => [ROOT_i, counter]
# AS => [[NEW_ACCOUNT_HASH_i, account_id_i]]

# Move counter down for next iteration
movdn.9
# => [ROOT_i, NEW_ACCOUNT_HASH_i, account_id_i, counter, ...]
# move the account hash to the operand stack and move it below the root
adv_push.4 swapw
# OS => [ROOT_i, NEW_ACCOUNT_HASH_i, counter]
# AS => [account_id_i, [NEW_ACCOUNT_HASH_{i+1}, account_id_{i+1}]]

# Prepare stack for `mtree_set`
movup.8 push.ACCOUNT_TREE_DEPTH
# => [account_tree_depth, account_id_i, ROOT_i, NEW_ACCOUNT_HASH_i, counter, ...]
# move the account id to the operand stack, push the account tree depth
adv_push.1 push.ACCOUNT_TREE_DEPTH
# OS => [account_tree_depth, account_id_i, ROOT_i, NEW_ACCOUNT_HASH_i, counter]
# AS => [[NEW_ACCOUNT_HASH_{i+1}, account_id_{i+1}]]

# set new value in SMT
mtree_set dropw
# => [ROOT_{i+1}, counter, ...]
# OS => [ROOT_{i+1}, counter]
# AS => [[NEW_ACCOUNT_HASH_{i+1}, account_id_{i+1}]]

# loop counter
movup.4 sub.1 dup neq.0
# => [0 or 1, counter-1, ROOT_{i+1}, ...]
movup.4 sub.1 dup movdn.5 neq.0
# OS => [flag, ROOT_{i+1}, counter]
# AS => [[NEW_ACCOUNT_HASH_{i+1}, account_id_{i+1}]]
end

drop
# => [ROOT_{n-1}]
# drop the counter
movup.4 drop
# OS => [ROOT_{n-1}]
# AS => []
end

#! Compute the note root.
#!
#! Each batch contains a tree of depth 10 for its created notes. The block's created notes tree is created
#! by aggregating up to 2^6 tree roots coming from the batches contained in the block.
#! Each batch contains a tree of depth 10 for its created notes. The block's created notes tree is
#! created by aggregating up to 2^6 tree roots coming from the batches contained in the block.
#!
#! `SMT_EMPTY_ROOT` must be `E16`, the root of the empty tree of depth 16. If less than 2^6 batches are
#! contained in the block, `E10` is used as the padding value; this is derived from the fact that
#! `SMT_EMPTY_ROOT` is `E16`, and that our tree has depth 6.
#! `SMT_EMPTY_ROOT` must be `E16`, the root of the empty tree of depth 16. If less than 2^6 batches
#! are contained in the block, `E10` is used as the padding value; this is derived from the fact
#! that `SMT_EMPTY_ROOT` is `E16`, and that our tree has depth 6.
#!
#! Stack: [num_notes_updated, SMT_EMPTY_ROOT,
#! batch_note_root_idx_0, BATCH_NOTE_TREE_ROOT_0,
#! ... ,
#! batch_note_root_idx_{n-1}, BATCH_NOTE_TREE_ROOT_{n-1}]
#! Output: [NOTES_ROOT]
#! Inputs:
#! Operand stack: []
#! Advice stack: [num_notes_updated, SMT_EMPTY_ROOT, [BATCH_NOTE_TREE_ROOT_i, batch_note_root_idx_i]]
#! Outputs:
#! Operand stack: [NOTES_ROOT]
proc.compute_note_root
# move the number of updated notes and empty root to the operand stack
adv_push.5
# OS => [SMT_EMPTY_ROOT, num_notes_updated]
# AS => [[BATCH_NOTE_TREE_ROOT_i, batch_note_root_idx_i]]

# assess if we should loop
dup neq.0
#=> [0 or 1, num_notes_updated, SMT_EMPTY_ROOT, ... ]
dup.4 neq.0
# OS => [flag, SMT_EMPTY_ROOT, num_notes_updated]
# AS => [[BATCH_NOTE_TREE_ROOT_i, batch_note_root_idx_i]]

while.true
#=> [note_roots_left_to_update, ROOT_i, batch_note_root_idx_i, BATCH_NOTE_TREE_ROOT_i, ... ]
# num_notes_updated here serves as a counter, so rename it accordingly
# empty root will be updated in each iteration, so rename it to the ROOT_i
# OS => [ROOT_i, counter]
# AS => [[BATCH_NOTE_TREE_ROOT_i, batch_note_root_idx_i]]

# Move counter down for next iteration
movdn.9
#=> [ROOT_i, batch_note_root_idx_i, BATCH_NOTE_TREE_ROOT_i, note_roots_left_to_update, ... ]
# move the batch note tree root to the operand stack and move it below the root
adv_push.4 swapw
# OS => [ROOT_i, BATCH_NOTE_TREE_ROOT_i, counter]
# AS => [batch_note_root_idx_i, [BATCH_NOTE_TREE_ROOT_{i+1}, batch_note_root_idx_{i+1}]]

# Prepare stack for mtree_set
movup.4 push.BLOCK_NOTES_BATCH_TREE_DEPTH
#=> [depth=batch_tree_depth, batch_note_root_idx_i, ROOT_i,
# BATCH_NOTE_TREE_ROOT_i, note_roots_left_to_update, ... ]
# move the batch note root index to the operand stack, push the block notes batch tree depth
adv_push.1 push.BLOCK_NOTES_BATCH_TREE_DEPTH
# OS => [batch_tree_depth, batch_note_root_idx_i, ROOT_i, BATCH_NOTE_TREE_ROOT_i, counter]
# AS => [[BATCH_NOTE_TREE_ROOT_{i+1}, batch_note_root_idx_{i+1}]]

# set new value in SMT
mtree_set dropw
#=> [ROOT_{i+1}, note_roots_left_to_update, ... ]

# OS => [ROOT_{i+1}, counter]
# AS => [[BATCH_NOTE_TREE_ROOT_{i+1}, batch_note_root_idx_{i+1}]]

# loop counter
movup.4 sub.1 dup neq.0
#=> [0 or 1, note_roots_left_to_update - 1, ROOT_{i+1}, ... ]
movup.4 sub.1 dup movdn.5 neq.0
# OS => [flag, ROOT_{i+1}, counter]
# AS => [[BATCH_NOTE_TREE_ROOT_{i+1}, batch_note_root_idx_{i+1}]]
end

drop
# => [ROOT_{n-1}]
# drop the counter
movup.4 drop
# OS => [ROOT_{n-1}]
# AS => []
end

#! Stack: [num_produced_nullifiers, OLD_NULLIFIER_ROOT, NULLIFIER_VALUE,
#! NULLIFIER_0, ..., NULLIFIER_n]
#! Output: [NULLIFIER_ROOT]
#! Compute the nullifier root.
#!
#! Inputs:
#! Operand stack: []
#! Advice stack: [num_produced_nullifiers, OLD_NULLIFIER_ROOT, NULLIFIER_VALUE, [NULLIFIER_i]]
#! Outputs:
#! Operand stack: [NULLIFIER_ROOT]
proc.compute_nullifier_root
# move the number of produced nullifiers, old root and nullifier value to the operand stack;
# move nullifier value below the root
adv_push.9 swapw
# OS => [OLD_NULLIFIER_ROOT, NULLIFIER_VALUE, num_produced_nullifiers]
# AS => [[NULLIFIER_i]]

# assess if we should loop
dup neq.0
#=> [0 or 1, num_produced_nullifiers, OLD_NULLIFIER_ROOT, NULLIFIER_VALUE, NULLIFIER_0, ..., NULLIFIER_n ]
dup.8 neq.0
# OS => [flag, OLD_NULLIFIER_ROOT, NULLIFIER_VALUE, num_produced_nullifiers]
# AS => [[NULLIFIER_i]]

while.true
#=> [num_nullifiers_left_to_update, ROOT_i, NULLIFIER_VALUE, NULLIFIER_i, ... ]
# num_produced_nullifiers here serves as a counter, so rename it accordingly
# old nullifier root will be updated in each iteration, so rename it to the ROOT_i
# OS => [ROOT_i, NULLIFIER_VALUE, counter]
# AS => [[NULLIFIER_i]]

# move the nullifier hash to the operand stack
adv_push.4
# OS => [NULLIFIER_i, ROOT_i, NULLIFIER_VALUE, counter]
# AS => [[NULLIFIER_{i+1}]]

# Prepare stack for `smt::set`
movdn.12 movupw.2 dupw.2
#=> [NULLIFIER_VALUE, NULLIFIER_i, ROOT_i, NULLIFIER_VALUE, num_nullifiers_left_to_update, ... ]
# dup the nullifier value
dupw.2
# OS => [NULLIFIER_VALUE, NULLIFIER_i, ROOT_i, NULLIFIER_VALUE, counter]
# AS => [[NULLIFIER_{i+1}]]

exec.smt::set
#=> [OLD_VALUE, ROOT_{i+1}, NULLIFIER_VALUE, num_nullifiers_left_to_update, ... ]
# OS => [OLD_VALUE, ROOT_{i+1}, NULLIFIER_VALUE, counter]
# AS => [[NULLIFIER_{i+1}]]

# Check that OLD_VALUE == 0 (i.e. that nullifier was indeed not previously produced)
assertz assertz assertz assertz
#=> [ROOT_{i+1}, NULLIFIER_VALUE, num_nullifiers_left_to_update, ... ]
# OS => [ROOT_{i+1}, NULLIFIER_VALUE, counter]
# AS => [[NULLIFIER_{i+1}]]

# loop counter
movup.8 sub.1 dup neq.0
#=> [0 or 1, num_nullifiers_left_to_update - 1, ROOT_{i+1}, NULLIFIER_VALUE, ... ]
movup.8 sub.1 dup movdn.9 neq.0
# OS => [flag, ROOT_{i+1}, NULLIFIER_VALUE, counter]
# AS => [[NULLIFIER_{i+1}]]
end
#=> [0, ROOT_{n-1}, NULLIFIER_VALUE ]

drop swapw dropw
# => [ROOT_{n-1}]
# drop the counter and the nullifier value
swapw dropw movup.4 drop
# OS => [ROOT_{n-1}]
# AS => []
end

#! Compute the chain MMR root
#!
#! Stack: [ PREV_CHAIN_MMR_HASH, PREV_BLOCK_HASH_TO_INSERT ]
#! Advice map: PREV_CHAIN_MMR_HASH -> NUM_LEAVES || peak_0 || .. || peak_{n-1} || <maybe padding>
#!
#! Output: [ CHAIN_MMR_ROOT ]
#! Inputs:
#! Operand stack: []
#! Advice stack: [PREV_BLOCK_HASH_TO_INSERT, PREV_CHAIN_MMR_HASH]
#! Advice map: {
#! PREV_CHAIN_MMR_HASH: [NUM_LEAVES, [peak_i], <maybe padding>]
#! }
#! Outputs:
#! Operand stack: [CHAIN_MMR_ROOT]
proc.compute_chain_mmr_root
# move the previous block hash and chain MMR hash to the operand stack
adv_push.8
# OS => [PREV_CHAIN_MMR_HASH, PREV_BLOCK_HASH_TO_INSERT]
# AS => []

# push chain MMR pointer to the operand stack
push.CHAIN_MMR_PTR movdn.4
# => [ PREV_CHAIN_MMR_HASH, chain_mmr_ptr, PREV_BLOCK_HASH_TO_INSERT ]
# OS => [PREV_CHAIN_MMR_HASH, chain_mmr_ptr, PREV_BLOCK_HASH_TO_INSERT]

# load the chain MMR (as of previous block) at memory location CHAIN_MMR_PTR
exec.mmr::unpack
# => [ PREV_BLOCK_HASH_TO_INSERT ]
# OS => [PREV_BLOCK_HASH_TO_INSERT]

# push chain MMR pointer to the operand stack
push.CHAIN_MMR_PTR movdn.4
# => [ PREV_BLOCK_HASH_TO_INSERT, chain_mmr_ptr ]
# OS => [PREV_BLOCK_HASH_TO_INSERT, chain_mmr_ptr]

# add PREV_BLOCK_HASH_TO_INSERT to chain MMR
exec.mmr::add
# => [ ]
# OS => []

# Compute new MMR root
push.CHAIN_MMR_PTR exec.mmr::pack
# => [ CHAIN_MMR_ROOT ]
# OS => [CHAIN_MMR_ROOT]
end

# Stack: [<account root inputs>, <note root inputs>, <nullifier root inputs>, <chain mmr root inputs>]
#! Inputs:
#! Operand stack: []
#! Advice stack: [<account root inputs>, <note root inputs>, <nullifier root inputs>, <chain mmr root inputs>]
#! Advice map: {
#! PREV_CHAIN_MMR_HASH: [NUM_LEAVES, [peak_i], <maybe padding>]
#! }
#! Outputs:
#! Operand stack: [ACCOUNT_ROOT, NOTE_ROOT, NULLIFIER_ROOT, CHAIN_MMR_ROOT]
begin
Comment on lines +216 to 224
Copy link
Contributor

Choose a reason for hiding this comment

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

This is fine for now, but we should be providing some inputs via the operand stack - otherwise this can be used to provide any arbitrary batch inputs.

Let's create 2 issues in miden-base for this: one issue for defining batch kernel inputs/outputs, and the other one for defining block kernel inputs/outputs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bobbinth I created two issue templates: 0xPolygonMiden/miden-base#919 and 0xPolygonMiden/miden-base#920, although didn't manage to fill them properly, I'm not sure I understand what should be done.

exec.compute_account_root mem_storew.0 dropw
# => [<note root inputs>, <nullifier root inputs>, <chain mmr root inputs>]

exec.compute_note_root mem_storew.1 dropw
# => [ <nullifier root inputs>, <chain mmr root inputs> ]
# => [<nullifier root inputs>, <chain mmr root inputs>]

exec.compute_nullifier_root mem_storew.2 dropw
# => [ <chain mmr root inputs> ]
# => [<chain mmr root inputs>]

exec.compute_chain_mmr_root
# => [ CHAIN_MMR_ROOT ]
# => [CHAIN_MMR_ROOT]

# Load output on stack
padw mem_loadw.2 padw mem_loadw.1 padw mem_loadw.0
#=> [ ACCOUNT_ROOT, NOTE_ROOT, NULLIFIER_ROOT, CHAIN_MMR_ROOT ]
end
# => [ACCOUNT_ROOT, NOTE_ROOT, NULLIFIER_ROOT, CHAIN_MMR_ROOT]
end
Loading
Loading