Skip to content

Commit

Permalink
BlockWitness: pass inputs to the VM through the advice provider (#516)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fumuran authored Oct 14, 2024
1 parent 167bbca commit 0e352bd
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 147 deletions.
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
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

0 comments on commit 0e352bd

Please sign in to comment.