-
Notifications
You must be signed in to change notification settings - Fork 139
feat: batch kernel skeleton #2904
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
Open
mmagician
wants to merge
20
commits into
next
Choose a base branch
from
mmagician-claude/batch-kernel-skeleton
base: next
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
1cbd5f9
feat: add skeleton batch kernel + ProvenBatch proof field
claude 0411259
Apply suggestions from code review
mmagician ee2df53
chore(protocol): drop premature BATCH error category
claude c508740
refactor(protocol): order build_input_stack params to match stack layout
claude 9ed96f7
refactor(batch-prover): remove unused proof_security_level accessor
claude 52adb85
refactor(protocol): simplify batch output padding check
claude ee3f559
test(batch): reuse shared chain setup helpers
claude e3d15c5
docs: trim verbose batch kernel CHANGELOG entry
claude ba9af23
Merge remote-tracking branch 'origin/next' into mmagician-claude/batc…
claude 0d20126
Merge remote-tracking branch 'origin/next' into mmagician-claude/batc…
claude 11f0760
refactor(protocol): name batch kernel inputs BATCH_ID and BLOCK_COMMI…
claude 7a4afcb
refactor(protocol): use Felt::ZERO associated constant
claude 26c044b
refactor(protocol): type batch kernel errors instead of stringifying
claude 7c72675
test(batch): rename batch_kernel module to test_batch_kernel
claude b84ea28
chore: shorten comment
mmagician 5e25d94
chore: fmt
mmagician d812d4d
refactor(batch-prover): split batch execution and proving
claude 7f3a5df
Merge branch 'next' into mmagician-claude/batch-kernel-skeleton
mmagician fc7d855
Update crates/miden-tx-batch-prover/src/batch_executor.rs
mmagician 79165e9
refactor(protocol): return a BatchOutput from parse_output_stack
claude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # MAIN | ||
| # ================================================================================================= | ||
|
|
||
| #! Batch kernel program (skeleton). | ||
| #! | ||
| #! A transaction batch groups a set of independently-proven transactions so they can later be | ||
| #! aggregated into a block by the block kernel. This program defines the public input/output | ||
| #! contract that the batch kernel will eventually verify, but currently does not yet perform | ||
| #! any verification: it drops its inputs and exits, leaving the all-zero word output region as the | ||
| #! stack's initial padding zeros. | ||
| #! | ||
| #! Inputs: [ | ||
| #! BATCH_ID, | ||
| #! BLOCK_COMMITMENT, | ||
| #! pad(8), | ||
| #! ] | ||
| #! | ||
| #! Outputs: [ | ||
| #! INPUT_NOTES_COMMITMENT, | ||
| #! OUTPUT_NOTES_COMMITMENT, | ||
| #! batch_expiration_block_num, | ||
| #! pad(7), | ||
| #! ] | ||
| #! | ||
| #! Where: | ||
| #! - BATCH_ID is the batch's `BatchId`, the commitment to its transactions. | ||
| #! - BLOCK_COMMITMENT is the commitment of the batch's reference block. | ||
| #! - INPUT_NOTES_COMMITMENT will be the sequential hash over every transaction's input note | ||
| #! commitments. In this skeleton it is the empty word. | ||
| #! - OUTPUT_NOTES_COMMITMENT will be the sequential hash over every transaction's output note | ||
| #! commitments. In this skeleton it is the empty word. | ||
| #! - batch_expiration_block_num will be the minimum of every transaction's | ||
| #! `expiration_block_num`. In this skeleton it is zero. | ||
| #! | ||
| proc main | ||
| dropw dropw | ||
| end | ||
|
|
||
| begin | ||
| exec.main | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| use alloc::vec::Vec; | ||
|
|
||
| use miden_core::program::Kernel; | ||
|
|
||
| use crate::batch::{BatchOutput, ProposedBatch}; | ||
| use crate::block::BlockNumber; | ||
| use crate::errors::BatchOutputError; | ||
| use crate::utils::serde::Deserializable; | ||
| use crate::utils::sync::LazyLock; | ||
| use crate::vm::{AdviceInputs, Program, ProgramInfo, StackInputs, StackOutputs}; | ||
| use crate::{Felt, Word}; | ||
|
|
||
| // CONSTANTS | ||
| // ================================================================================================ | ||
|
|
||
| static KERNEL_MAIN: LazyLock<Program> = LazyLock::new(|| { | ||
| let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/assets/kernels/batch_kernel.masb")); | ||
| Program::read_from_bytes(bytes).expect("failed to deserialize batch kernel runtime") | ||
| }); | ||
|
|
||
| // BATCH KERNEL | ||
| // ================================================================================================ | ||
|
|
||
| /// The batch kernel program: an executable Miden program that proves a batch of transactions. | ||
| /// | ||
| /// The kernel takes `[BATCH_ID, BLOCK_COMMITMENT]` as public inputs and emits | ||
| /// `[INPUT_NOTES_COMMITMENT, OUTPUT_NOTES_COMMITMENT, batch_expiration_block_num]`. See | ||
| /// `asm/kernels/batch/main.masm` for the input/output contract. | ||
| pub struct BatchKernel; | ||
|
|
||
| impl BatchKernel { | ||
| // KERNEL SOURCE CODE | ||
| // -------------------------------------------------------------------------------------------- | ||
|
|
||
| /// Returns the executable batch kernel program loaded from the build's `OUT_DIR`. | ||
| pub fn main() -> Program { | ||
| KERNEL_MAIN.clone() | ||
| } | ||
|
|
||
| /// Returns [`ProgramInfo`] for the batch kernel program. | ||
| /// | ||
| /// The batch kernel does not expose syscalls, so the associated [`Kernel`] is empty. | ||
| pub fn program_info() -> ProgramInfo { | ||
| ProgramInfo::new(Self::main().hash(), Kernel::default()) | ||
| } | ||
|
|
||
| // INPUT BUILDERS | ||
| // -------------------------------------------------------------------------------------------- | ||
|
|
||
| /// Transforms the provided [`ProposedBatch`] into the stack and advice inputs needed to | ||
| /// execute the batch kernel. | ||
| pub fn prepare_inputs(proposed_batch: &ProposedBatch) -> (StackInputs, AdviceInputs) { | ||
| let block_commitment = proposed_batch.reference_block_header().commitment(); | ||
| let batch_id = proposed_batch.id().as_word(); | ||
|
|
||
| let stack_inputs = Self::build_input_stack(batch_id, block_commitment); | ||
| let advice_inputs = Self::build_advice_inputs(proposed_batch); | ||
|
|
||
| (stack_inputs, advice_inputs) | ||
| } | ||
|
|
||
| /// Returns the stack with the public inputs required by the batch kernel. | ||
| /// | ||
| /// The initial stack is: | ||
| /// | ||
| /// ```text | ||
| /// [BATCH_ID, BLOCK_COMMITMENT, pad(8)] | ||
| /// ``` | ||
| /// | ||
| /// Where: | ||
| /// - `BATCH_ID` is the batch's [`BatchId`](crate::batch::BatchId). | ||
| /// - `BLOCK_COMMITMENT` is the commitment of the batch's reference block. | ||
| pub fn build_input_stack(batch_id: Word, block_commitment: Word) -> StackInputs { | ||
| let mut inputs: Vec<Felt> = Vec::with_capacity(8); | ||
| inputs.extend_from_slice(batch_id.as_elements()); | ||
| inputs.extend_from_slice(block_commitment.as_elements()); | ||
|
|
||
| StackInputs::new(&inputs).expect("number of stack inputs should be <= 16") | ||
| } | ||
|
|
||
| /// Builds the stack with the expected batch kernel outputs. | ||
| /// | ||
| /// The output stack is defined as: | ||
| /// | ||
| /// ```text | ||
| /// [INPUT_NOTES_COMMITMENT, OUTPUT_NOTES_COMMITMENT, batch_expiration_block_num] | ||
| /// ``` | ||
| pub fn build_output_stack( | ||
| input_notes_commitment: Word, | ||
| output_notes_commitment: Word, | ||
| batch_expiration_block_num: BlockNumber, | ||
| ) -> StackOutputs { | ||
| let mut outputs: Vec<Felt> = Vec::with_capacity(9); | ||
| outputs.extend_from_slice(input_notes_commitment.as_elements()); | ||
| outputs.extend_from_slice(output_notes_commitment.as_elements()); | ||
| outputs.push(Felt::from(batch_expiration_block_num)); | ||
|
|
||
| StackOutputs::new(&outputs).expect("number of stack outputs should be <= 16") | ||
| } | ||
|
|
||
| /// Extracts the [`BatchOutput`] from the provided stack outputs. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns an error if: | ||
| /// - The padding cells (positions 9..16) are not all zero. | ||
| /// - `batch_expiration_block_num` does not fit into a `u32`. | ||
| pub fn parse_output_stack(stack: &StackOutputs) -> Result<BatchOutput, BatchOutputError> { | ||
| let input_notes_commitment = stack | ||
|
mmagician marked this conversation as resolved.
|
||
| .get_word(BatchOutput::INPUT_NOTES_COMMITMENT_WORD_IDX) | ||
| .expect("input_notes_commitment word missing"); | ||
| let output_notes_commitment = stack | ||
| .get_word(BatchOutput::OUTPUT_NOTES_COMMITMENT_WORD_IDX) | ||
| .expect("output_notes_commitment word missing"); | ||
|
|
||
| let expiration_felt = stack | ||
| .get_element(BatchOutput::BATCH_EXPIRATION_BLOCK_NUM_ELEMENT_IDX) | ||
| .expect("batch_expiration_block_num missing"); | ||
|
|
||
| // Every cell after batch_expiration_block_num must be zero padding. | ||
| if stack[BatchOutput::BATCH_EXPIRATION_BLOCK_NUM_ELEMENT_IDX + 1..] | ||
| .iter() | ||
| .any(|&felt| felt != Felt::ZERO) | ||
| { | ||
| return Err(BatchOutputError::OutputStackInvalid( | ||
| "batch_expiration_block_num must be followed by zero padding".into(), | ||
| )); | ||
| } | ||
|
|
||
| let batch_expiration_block_num = u32::try_from(expiration_felt.as_canonical_u64()) | ||
| .map_err(|_| BatchOutputError::ExpirationBlockNumberTooLarge(expiration_felt))? | ||
| .into(); | ||
|
|
||
| Ok(BatchOutput::new( | ||
| input_notes_commitment, | ||
| output_notes_commitment, | ||
| batch_expiration_block_num, | ||
| )) | ||
| } | ||
|
|
||
| // ADVICE BUILDER | ||
| // -------------------------------------------------------------------------------------------- | ||
|
|
||
| /// Builds the advice inputs (map + stack) consumed by the batch kernel. | ||
| /// | ||
| /// The skeleton kernel ignores its advice inputs, so this returns the default empty value. | ||
| fn build_advice_inputs(_proposed_batch: &ProposedBatch) -> AdviceInputs { | ||
| AdviceInputs::default() | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| use crate::Word; | ||
| use crate::block::BlockNumber; | ||
|
|
||
| // BATCH OUTPUT | ||
| // ================================================================================================ | ||
|
|
||
| /// The public outputs produced by the batch kernel. | ||
| /// | ||
| /// This is the parsed, typed form of the kernel's output stack (see | ||
| /// [`BatchKernel::parse_output_stack`](crate::batch::BatchKernel::parse_output_stack)), mirroring | ||
| /// [`TransactionOutputs`](crate::transaction::TransactionOutputs) for transactions. | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub struct BatchOutput { | ||
| /// The commitment to the batch's input notes. | ||
| input_notes_commitment: Word, | ||
| /// The commitment to the batch's output notes. | ||
| output_notes_commitment: Word, | ||
| /// The block number at which the batch expires. | ||
| batch_expiration_block_num: BlockNumber, | ||
| } | ||
|
|
||
| impl BatchOutput { | ||
| // OUTPUT STACK LAYOUT | ||
| // -------------------------------------------------------------------------------------------- | ||
|
|
||
| /// The element index at which the input notes commitment word starts on the output stack. | ||
| pub const INPUT_NOTES_COMMITMENT_WORD_IDX: usize = 0; | ||
| /// The element index at which the output notes commitment word starts on the output stack. | ||
| pub const OUTPUT_NOTES_COMMITMENT_WORD_IDX: usize = 4; | ||
| /// The element index at which the batch expiration block number is stored on the output stack. | ||
| pub const BATCH_EXPIRATION_BLOCK_NUM_ELEMENT_IDX: usize = 8; | ||
|
|
||
| // CONSTRUCTOR | ||
| // -------------------------------------------------------------------------------------------- | ||
|
|
||
| /// Returns a new [`BatchOutput`] instantiated from the provided data. | ||
| pub fn new( | ||
| input_notes_commitment: Word, | ||
| output_notes_commitment: Word, | ||
| batch_expiration_block_num: BlockNumber, | ||
| ) -> Self { | ||
| Self { | ||
| input_notes_commitment, | ||
| output_notes_commitment, | ||
| batch_expiration_block_num, | ||
| } | ||
| } | ||
|
|
||
| // PUBLIC ACCESSORS | ||
| // -------------------------------------------------------------------------------------------- | ||
|
|
||
| /// Returns the commitment to the batch's input notes. | ||
| pub fn input_notes_commitment(&self) -> Word { | ||
| self.input_notes_commitment | ||
| } | ||
|
|
||
| /// Returns the commitment to the batch's output notes. | ||
| pub fn output_notes_commitment(&self) -> Word { | ||
| self.output_notes_commitment | ||
| } | ||
|
|
||
| /// Returns the block number at which the batch expires. | ||
| pub fn batch_expiration_block_num(&self) -> BlockNumber { | ||
| self.batch_expiration_block_num | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the original intention here was for the batch kernel to output the root of the
BatchNoteTree, rather than just a commitment to the output notes.This way, the block kernel would have to do much less work and could aggregate the individual
BatchNoteTrees into a singleBlockNoteTree. This would have to be the MASM equivalent ofBlockNoteTree::insert_batch_note_subtree.I think the reason we don't currently do this at block level, is because we decided to also have note erasure at block level, and so if the block erases on of the batch's notes, the tree root would become stale and would have to be updated. Doing this update is probably still better than recomputing the entire tree from scratch, but we may want to think about that.
To start simple, I see two routes:
BatchNoteTreeroot and aggregate at block level, don't do note erasure at block level for now.I think the first route is a bit closer to the end goal, so I'd go with that one. Just a suggestion, though.
I think I'm skeptical of the usefulness of note erasure at block level, given that batches are already incentivized to do as much note erasure as possible, and it just adds extra complexity.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, my guess is that this will rarely if ever be used in practice. I'd be in favor of dropping it. Created #3008
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I know, I should have maybe mentioned this in the comments that the first version would just have a commitment to all output notes instead. Mostly because, AFAICS, the
BatchNoteTreeis not actually wired up anywhere during batch construction.So what I'd suggest is:
OUTPUT_NOTES_COMMITMENTBatchNoteTreeas part ofProposedBatch::new()BATCH_NOTE_TREE_COMMITMENTWDYT? If so I'll create an issue for 2.