Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
- Added `active_note::get_storage_info` and `active_note::get_bounded_storage`, and switched the standard and agglayer note scripts with a bounded storage layout over to the latter ([#3563](https://github.com/0xMiden/protocol/pull/3563)).
- [BREAKING] AggLayer bridge and faucet accounts now map note repricing to an initial `FEE_MNGR` role instead of the built-in `ADMIN` role ([#3571](https://github.com/0xMiden/protocol/issues/3571)).
- [BREAKING] AggLayer bridge accounts now map emergency pause to an initial `PAUSER` role, while unpause remains restricted to `ADMIN` ([#3572](https://github.com/0xMiden/protocol/issues/3572)).
- Added the block kernel skeleton, establishing its public input/output contract and the `BlockExecutor` that runs it ([#1706](https://github.com/0xMiden/protocol/issues/1706)).

### Changes

- [BREAKING] `BlockProof` now carries an `ExecutionProof` and `LocalBlockProver::prove` takes an `ExecutedBlock`, replacing the placeholder that ignored its arguments ([#1706](https://github.com/0xMiden/protocol/issues/1706)).
- [BREAKING] Refactored `AccountVaultDelta` to track generic assets. `FungibleAssetDelta`, `NonFungibleAssetDelta` and `NonFungibleDeltaAction` were removed ([3485](https://github.com/0xMiden/protocol/pull/3485)).
- [BREAKING] Moved the internal shared helpers of `miden::protocol::input_note`, `miden::protocol::active_note`, and the note memory-write helpers into private `input_note_internal` and `note_internal` modules ([#3501](https://github.com/0xMiden/protocol/pull/3501)).
- [BREAKING] Changed asset callbacks into validation-only interfaces that return no asset value; the transaction kernel retains and uses the original value, preventing callbacks from modifying it. The kernel commitment changes ([#3505](https://github.com/0xMiden/protocol/issues/3505), [#3513](https://github.com/0xMiden/protocol/pull/3513)).
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions crates/miden-block-prover/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ bench = false
doctest = false

[features]
testing = []
testing = ["miden-processor/testing", "miden-protocol/testing"]

[dependencies]
miden-protocol = { workspace = true }
thiserror = { workspace = true }
miden-processor = { workspace = true }
miden-protocol = { workspace = true }
miden-prover = { workspace = true }
thiserror = { workspace = true }
52 changes: 52 additions & 0 deletions crates/miden-block-prover/src/block_executor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use miden_processor::{DefaultHost, ExecutionError, ExecutionOptions, FastProcessor};
use miden_protocol::block::{BlockKernel, BlockOutputs, ProposedBlock};

use crate::{BlockProverError, ExecutedBlock};

// BLOCK EXECUTOR
// ================================================================================================

/// Executes the block kernel over a [`ProposedBlock`], producing an [`ExecutedBlock`].
#[derive(Clone, Default)]
pub struct BlockExecutor;

impl BlockExecutor {
/// Creates a new [`BlockExecutor`] instance.
pub fn new() -> Self {
Self
}

/// Runs the block kernel over the [`ProposedBlock`], returning an [`ExecutedBlock`] that can be
/// passed to [`LocalBlockProver::prove`](crate::LocalBlockProver::prove).
///
/// # Errors
///
/// Returns an error if:
/// - the block kernel program fails to execute;
/// - the kernel output stack fails to parse.
pub fn execute(
&self,
proposed_block: ProposedBlock,
) -> Result<ExecutedBlock, BlockProverError> {
let (stack_inputs, advice_inputs) = BlockKernel::prepare_inputs(&proposed_block);

let processor = FastProcessor::new_with_options(
stack_inputs,
advice_inputs,
ExecutionOptions::default(),
)
.map_err(ExecutionError::advice_error_no_context)
.map_err(BlockProverError::BlockKernelExecutionFailed)?;

let trace_inputs = processor
.execute_trace_inputs_sync(&BlockKernel::main(), &mut DefaultHost::default())
.map_err(BlockProverError::BlockKernelExecutionFailed)?;

// Parse and validate the output stack shape (padding cells are zero); the actual output
// values themselves are not checked until the kernel computes them.
let block_outputs = BlockOutputs::parse(trace_inputs.stack_outputs())
.map_err(BlockProverError::BlockKernelOutputInvalid)?;

Ok(ExecutedBlock::new(proposed_block, trace_inputs, block_outputs))
}
}
14 changes: 10 additions & 4 deletions crates/miden-block-prover/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
use miden_processor::ExecutionError;
use miden_protocol::errors::BlockOutputError;

// BLOCK PROVER ERROR
// ================================================================================================

/// Represents errors that can occur during block proving.
///
/// NOTE: Block proving is not yet implemented. This is a placeholder enum.
/// Represents errors that can occur during block execution and proving.
#[derive(Debug, thiserror::Error)]
pub enum BlockProverError {}
pub enum BlockProverError {
#[error("block kernel execution failed")]
BlockKernelExecutionFailed(#[source] ExecutionError),
#[error("block kernel produced an invalid output stack")]
BlockKernelOutputInvalid(#[source] BlockOutputError),
}
47 changes: 47 additions & 0 deletions crates/miden-block-prover/src/executed_block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use miden_processor::TraceBuildInputs;
use miden_protocol::block::{BlockOutputs, ProposedBlock};

// EXECUTED BLOCK
// ================================================================================================

/// A [`ProposedBlock`] whose block kernel has been executed, but not yet proven.
///
/// Produced by [`BlockExecutor::execute`](crate::BlockExecutor::execute) and consumed by
/// [`LocalBlockProver::prove`](crate::LocalBlockProver::prove). It carries the executed block's
/// trace inputs so that proving only needs to build the trace and generate the proof.
pub struct ExecutedBlock {
proposed_block: ProposedBlock,
trace_inputs: TraceBuildInputs,
block_outputs: BlockOutputs,
}

impl ExecutedBlock {
/// Creates a new [`ExecutedBlock`] from the proposed block, the trace inputs and the public
/// outputs produced by executing the block kernel over it.
pub(crate) fn new(
proposed_block: ProposedBlock,
trace_inputs: TraceBuildInputs,
block_outputs: BlockOutputs,
) -> Self {
Self {
proposed_block,
trace_inputs,
block_outputs,
}
}

/// Returns the [`ProposedBlock`] this block was executed from.
pub fn proposed_block(&self) -> &ProposedBlock {
&self.proposed_block
}

/// Returns the public outputs produced by the block kernel.
pub fn block_outputs(&self) -> &BlockOutputs {
&self.block_outputs
}

/// Consumes the executed block, returning the trace inputs needed to prove it.
pub(crate) fn into_trace_inputs(self) -> TraceBuildInputs {
self.trace_inputs
}
}
10 changes: 8 additions & 2 deletions crates/miden-block-prover/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
mod local_block_prover;
pub use local_block_prover::LocalBlockProver;
mod block_executor;
pub use block_executor::BlockExecutor;

mod errors;
pub use errors::BlockProverError;

mod executed_block;
pub use executed_block::ExecutedBlock;

mod local_block_prover;
pub use local_block_prover::LocalBlockProver;
71 changes: 44 additions & 27 deletions crates/miden-block-prover/src/local_block_prover.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,63 @@
use miden_protocol::batch::OrderedBatches;
use miden_protocol::block::{BlockHeader, BlockInputs, BlockProof};
use miden_protocol::block::BlockProof;
use miden_prover::{ProvingOptions, TraceProvingInputs, prove_from_trace_sync};

use crate::BlockProverError;
use crate::{BlockProverError, ExecutedBlock};

// LOCAL BLOCK PROVER
// ================================================================================================

/// A local prover for blocks in the chain.
#[derive(Clone)]
pub struct LocalBlockProver {}
///
/// Proves an [`ExecutedBlock`] produced by [`BlockExecutor`](crate::BlockExecutor) into a
/// [`BlockProof`] over the block's public commitments.
///
/// # Warning
///
/// The current block kernel is a skeleton that drops its inputs and emits an all-zero output
/// region, so the produced proof attests only that the kernel program ran over the block's
/// `[PREV_BLOCK_COMMITMENT, BATCHES_COMMITMENT]` public inputs. It does **not** yet bind the
/// block's account updates, notes or nullifiers, so a block whose contents were mutated would
/// still carry a valid proof. This must therefore not be relied on at a trust boundary until the
/// kernel verification logic that emits and binds the real commitments lands.
#[derive(Clone, Default)]
pub struct LocalBlockProver {
proving_options: ProvingOptions,
}

impl LocalBlockProver {
/// Creates a new [`LocalBlockProver`] instance.
pub fn new(_proof_security_level: u32) -> Self {
// TODO: This will eventually take the security level as a parameter, but until we verify
// batches it is ignored.
Self {}
// blocks it is ignored.
Self::default()
}

/// Generates a proof of a block in the chain based on the given header and inputs.
/// Proves the [`ExecutedBlock`] into a [`BlockProof`].
///
/// Builds the execution trace from the executed block and generates the proof.
///
/// # Errors
///
/// NOTE: Block proving is not yet implemented. This is a placeholder struct.
pub fn prove(
&self,
_tx_batches: OrderedBatches,
_block_header: &BlockHeader,
_block_inputs: BlockInputs,
) -> Result<BlockProof, BlockProverError> {
Ok(BlockProof {})
/// Returns an error if proof generation fails.
pub fn prove(&self, executed_block: ExecutedBlock) -> Result<BlockProof, BlockProverError> {
let trace_inputs = executed_block.into_trace_inputs();

let (_stack_outputs, proof) = prove_from_trace_sync(TraceProvingInputs::new(
trace_inputs,
self.proving_options.clone(),
))
.map_err(BlockProverError::BlockKernelExecutionFailed)?;

Ok(BlockProof::new(proof))
}

/// A mock implementation of the execution of a proof of a block in the chain based on the given
/// header and inputs.
/// Returns a [`BlockProof`] carrying a dummy execution proof, without running the block kernel.
///
/// This is exposed for testing purposes.
#[cfg(any(feature = "testing", test))]
pub fn prove_dummy(
&self,
_tx_batches: OrderedBatches,
_block_header: BlockHeader,
_block_inputs: BlockInputs,
) -> Result<BlockProof, BlockProverError> {
Ok(BlockProof {})
/// This is exposed for testing purposes. It is gated on the `testing` feature alone rather
/// than also on `cfg(test)`, because [`BlockProof::new_dummy`] requires `miden-protocol`'s own
/// `testing` feature, which only this crate's `testing` feature turns on.
#[cfg(feature = "testing")]
pub fn prove_dummy(&self) -> BlockProof {
BlockProof::new_dummy()
}
}
6 changes: 6 additions & 0 deletions crates/miden-protocol/asm/kernels/block/miden-project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "miden-block-kernel"
version.workspace = true

[[bin]]
path = "src/main.masm"
31 changes: 31 additions & 0 deletions crates/miden-protocol/asm/kernels/block/src/main.masm
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# MAIN
# =================================================================================================

#! Block kernel program (skeleton).
#!
#! A block aggregates a set of independently-proven transaction batches into the next block of the
#! chain. This program defines the public input/output contract that the block 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: [PREV_BLOCK_COMMITMENT, BATCHES_COMMITMENT, pad(8)]
#! Outputs: [BLOCK_COMMITMENT, NULLIFIER_COMMITMENT, pad(8)]
#!
#! Where:
#! - PREV_BLOCK_COMMITMENT is the commitment of the block header this block builds on top of.
#! - BATCHES_COMMITMENT is the sequential hash over the `BatchId`s of the batches in this block. It
#! pins both which batches the block contains and the order they appear in.
#! - BLOCK_COMMITMENT is the commitment of the newly created block header, which in turn
#! commits to the block's account root, nullifier root, note root, chain commitment and
#! transaction commitment. In this skeleton it is the empty word.
#! - NULLIFIER_COMMITMENT is the commitment to the set of nullifiers created in this block. In
#! this skeleton it is the empty word.
#!
proc main
dropw dropw
# => [pad(16)]
end

begin
exec.main
end
9 changes: 8 additions & 1 deletion crates/miden-protocol/asm/miden-project.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
[workspace]
members = ["kernels/batch", "kernels/transaction", "kernels/transaction-core", "protocol", "protocol_utils"]
members = [
"kernels/batch",
"kernels/block",
"kernels/transaction",
"kernels/transaction-core",
"protocol",
"protocol_utils",
]

[workspace.package]
version = "0.16.0"
Expand Down
13 changes: 12 additions & 1 deletion crates/miden-protocol/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ const ASM_PROTOCOL_UTILS_DIR: &str = "protocol_utils";
const ASM_TX_KERNEL_DIR: &str = "kernels/transaction";
const ASM_TX_KERNEL_CORE_DIR: &str = "kernels/transaction-core";
const ASM_BATCH_KERNEL_DIR: &str = "kernels/batch";
const ASM_BLOCK_KERNEL_DIR: &str = "kernels/block";

// Executable target names, as declared in the respective `miden-project.toml` files.
const TX_KERNEL_MAIN_TARGET: &str = "main";
const TX_SCRIPT_MAIN_TARGET: &str = "tx-script-main";
const BATCH_KERNEL_TARGET: &str = "miden-batch-kernel";
const BLOCK_KERNEL_TARGET: &str = "miden-block-kernel";

/// Module of the kernel package that holds the procedures which `exec_kernel_proc` invokes.
const KERNEL_API_MODULE_PATH: &str = "$kernel::api";
Expand Down Expand Up @@ -72,7 +74,7 @@ const TX_KERNEL_ERROR_CATEGORIES: [&str; 14] = [
///
/// Assembles the Miden projects defined by the `miden-project.toml` files in the `asm` directory
/// into MAST packages (.masp files): the transaction kernel library and executables, the batch
/// kernel executable, and the user-facing protocol library.
/// and block kernel executables, and the user-facing protocol library.
fn main() -> Result<()> {
// re-build when the MASM code changes
println!("cargo::rerun-if-changed={ASM_DIR}/");
Expand Down Expand Up @@ -107,6 +109,15 @@ fn main() -> Result<()> {
&target_dir.join("kernels"),
)?;

// compile block kernel
let manifest_path = source_dir.join(ASM_BLOCK_KERNEL_DIR).join(PROJECT_MANIFEST);
assemble_project(
manifest_path,
ProjectTargetSelector::Executable(BLOCK_KERNEL_TARGET),
&mut store,
&target_dir.join("kernels"),
)?;

generate_error_constants(&source_dir, &build_dir)?;

// extract the event definitions from the MASM sources and generate their constants
Expand Down
14 changes: 14 additions & 0 deletions crates/miden-protocol/src/batch/ordered_batches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::utils::serde::{
DeserializationError,
Serializable,
};
use crate::{Hasher, Word};

// ORDERED BATCHES
// ================================================================================================
Expand All @@ -32,6 +33,19 @@ impl OrderedBatches {
&self.0
}

/// Computes a commitment to the batches in this block.
///
/// This is a sequential hash over the [`BatchId`](crate::batch::BatchId) of each batch, in
/// order.
pub fn commitment(&self) -> Word {
Comment thread
Fumuran marked this conversation as resolved.
Outdated
let mut elements = Vec::with_capacity(self.0.len() * Word::NUM_ELEMENTS);
for batch in self.0.iter() {
elements.extend_from_slice(batch.id().as_word().as_elements());
}

Hasher::hash_elements(&elements)
}

/// Converts the transactions in batches into ordered transaction headers.
pub fn to_transactions(&self) -> OrderedTransactionHeaders {
OrderedTransactionHeaders::new_unchecked(
Expand Down
Loading
Loading