Skip to content

Commit be707bc

Browse files
committed
test: drive batch-kernel rejection tests through BatchExecutor advice injection
Add BatchExecutor::extend_advice_inputs (mirroring TransactionContextBuilder) so the kernel's rejection paths can be exercised through the normal executor with tampered advice, and drop the low-level run_kernel test helper. The three negative tests now corrupt the Layer 1/2/3 advice-map entries via the executor.
1 parent 59c2efb commit be707bc

2 files changed

Lines changed: 55 additions & 58 deletions

File tree

crates/miden-testing/src/kernel_tests/batch/test_batch_kernel.rs

Lines changed: 35 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ use alloc::vec::Vec;
33
use std::collections::BTreeMap;
44

55
use anyhow::Context;
6-
use miden_core_lib::CoreLibrary;
7-
use miden_processor::{DefaultHost, ExecutionOptions, FastProcessor};
8-
use miden_protocol::batch::{BatchId, BatchKernel, ProposedBatch};
6+
use miden_protocol::batch::{BatchKernel, ProposedBatch};
97
use miden_protocol::block::BlockNumber;
10-
use miden_protocol::vm::{AdviceInputs, StackInputs, StackOutputs};
8+
use miden_protocol::vm::AdviceInputs;
119
use miden_protocol::{Felt, Hasher, Word};
1210
use miden_tx_batch_prover::{BatchExecutor, LocalBatchProver};
1311

@@ -83,30 +81,24 @@ fn expected_input_notes_commitment(batch: &ProposedBatch) -> Word {
8381
}
8482
}
8583

86-
// EXECUTION HELPERS
84+
// TAMPERING HELPERS
8785
// ================================================================================================
8886

89-
/// Runs the batch kernel directly over the given inputs, returning its output stack.
90-
///
91-
/// Used by the tampering tests, which corrupt the advice inputs before execution. `BatchExecutor`
92-
/// builds the advice internally from a (valid) `ProposedBatch` and offers no injection point, so it
93-
/// cannot exercise the kernel's rejection paths. This mirrors how the transaction-kernel tests
94-
/// inject tampered advice and run kernel code directly (see `tx_context.execute_code` with
95-
/// `extend_advice_inputs` in `test_prologue.rs`).
96-
fn run_kernel(
97-
stack_inputs: StackInputs,
98-
advice_inputs: AdviceInputs,
99-
) -> Result<StackOutputs, miden_processor::ExecutionError> {
100-
let mut host = DefaultHost::default();
101-
host.load_library(CoreLibrary::default().mast_forest())
102-
.expect("loading the core library into the test host should succeed");
103-
104-
let processor =
105-
FastProcessor::new_with_options(stack_inputs, advice_inputs, ExecutionOptions::default())
106-
.expect("failed to create processor")
107-
.with_debugging(true);
108-
let output = processor.execute_sync(&BatchKernel::main(), &mut host)?;
109-
Ok(output.stack)
87+
/// Builds an advice-inputs override that corrupts the advice-map entry stored under `key`, so the
88+
/// kernel's hash check against `key` fails. Fed to [`BatchExecutor::extend_advice_inputs`] to drive
89+
/// the kernel's rejection paths through the normal executor (mirroring how the transaction-kernel
90+
/// tests inject tampered advice via `extend_advice_inputs`; see `test_prologue.rs`).
91+
fn tampered_advice_for(batch: &ProposedBatch, key: Word) -> AdviceInputs {
92+
let (_, advice_inputs) = BatchKernel::prepare_inputs(batch);
93+
let mut tampered: Vec<Felt> = advice_inputs
94+
.map
95+
.get(&key)
96+
.expect("advice-map entry for key")
97+
.iter()
98+
.copied()
99+
.collect();
100+
tampered[0] += Felt::from(1u32);
101+
AdviceInputs::default().with_map([(key, tampered)])
110102
}
111103

112104
// HAPPY PATH
@@ -148,63 +140,51 @@ fn batch_executor_then_prover_produces_proven_batch() -> anyhow::Result<()> {
148140

149141
// NEGATIVE TESTS
150142
// ================================================================================================
143+
//
144+
// Each test injects a tampered advice-map entry through `BatchExecutor::extend_advice_inputs` and
145+
// asserts the kernel aborts. The executor builds consistent advice from the (valid) `ProposedBatch`
146+
// and the override then corrupts a single layer's entry, breaking that layer's hash check.
151147

152-
/// Corrupting `BATCH_ID` on the input stack makes Layer 1 unloadable from the advice map, so the
153-
/// kernel must abort.
148+
/// Tampering the `BATCH_ID` -> `(tx_id, account_id)` tuples breaks the Layer 1 hash check.
154149
#[test]
155-
fn batch_kernel_rejects_wrong_batch_id() -> anyhow::Result<()> {
150+
fn batch_kernel_rejects_tampered_layer_1() -> anyhow::Result<()> {
156151
let mut setup = setup_chain();
157152
let batch = two_tx_batch(&mut setup)?;
158153

159-
let block_commitment = batch.reference_block_header().commitment();
160-
// A BatchId over a one-transaction subset differs from the real (two-tx) batch id, so the
161-
// kernel cannot find its Layer 1 tuples in the advice map.
162-
let bogus_tx = &batch.transactions()[0];
163-
let bogus_batch_id = BatchId::from_ids([(bogus_tx.id(), bogus_tx.account_id())]);
164-
let stack_inputs = BatchKernel::build_input_stack(block_commitment, bogus_batch_id);
165-
let (_, advice_inputs) = BatchKernel::prepare_inputs(&batch);
154+
let override_advice = tampered_advice_for(&batch, batch.id().as_word());
166155

167-
run_kernel(stack_inputs, advice_inputs).expect_err("kernel must abort on an unknown BATCH_ID");
156+
let result = BatchExecutor::new().extend_advice_inputs(override_advice).execute(batch);
157+
assert!(result.is_err(), "kernel must abort on a tampered transaction list");
168158

169159
Ok(())
170160
}
171161

172-
/// Tampering a verified `tx_id`'s Layer 2 advice-map entry breaks the per-tx header hash check.
162+
/// Tampering a verified `tx_id`'s header data breaks the Layer 2 hash check.
173163
#[test]
174164
fn batch_kernel_rejects_tampered_layer_2() -> anyhow::Result<()> {
175165
let mut setup = setup_chain();
176166
let batch = two_tx_batch(&mut setup)?;
177167

178-
let (stack_inputs, mut advice_inputs) = BatchKernel::prepare_inputs(&batch);
179-
180168
let tx0_id = batch.transactions()[0].id().as_word();
181-
let entry = advice_inputs.map.get(&tx0_id).expect("tx0 layer 2 entry");
182-
let mut tampered: Vec<Felt> = entry.iter().copied().collect();
183-
tampered[0] += Felt::from(1u32);
184-
advice_inputs.map.extend([(tx0_id, tampered)]);
169+
let override_advice = tampered_advice_for(&batch, tx0_id);
185170

186-
run_kernel(stack_inputs, advice_inputs)
187-
.expect_err("kernel must abort on a tampered transaction header");
171+
let result = BatchExecutor::new().extend_advice_inputs(override_advice).execute(batch);
172+
assert!(result.is_err(), "kernel must abort on a tampered transaction header");
188173

189174
Ok(())
190175
}
191176

192-
/// Tampering the per-tx input-notes Layer 3 entry breaks the input-note hash check.
177+
/// Tampering a transaction's input-notes data breaks the Layer 3 (input-notes commitment) check.
193178
#[test]
194179
fn batch_kernel_rejects_tampered_input_notes() -> anyhow::Result<()> {
195180
let mut setup = setup_chain();
196181
let batch = two_tx_batch(&mut setup)?;
197182

198-
let (stack_inputs, mut advice_inputs) = BatchKernel::prepare_inputs(&batch);
199-
200183
let key = batch.transactions()[0].input_notes().commitment();
201-
let entry = advice_inputs.map.get(&key).expect("layer 3 entry");
202-
let mut tampered: Vec<Felt> = entry.iter().copied().collect();
203-
tampered[0] += Felt::from(1u32);
204-
advice_inputs.map.extend([(key, tampered)]);
184+
let override_advice = tampered_advice_for(&batch, key);
205185

206-
run_kernel(stack_inputs, advice_inputs)
207-
.expect_err("kernel must abort on tampered input notes data");
186+
let result = BatchExecutor::new().extend_advice_inputs(override_advice).execute(batch);
187+
assert!(result.is_err(), "kernel must abort on tampered input-notes data");
208188

209189
Ok(())
210190
}

crates/miden-tx-batch-prover/src/batch_executor.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use miden_processor::{DefaultHost, ExecutionError, ExecutionOptions, FastProcess
22
use miden_protocol::CoreLibrary;
33
use miden_protocol::batch::{BatchKernel, BatchOutputs, ProposedBatch};
44
use miden_protocol::errors::ProvenBatchError;
5+
use miden_protocol::vm::AdviceInputs;
56

67
use crate::ExecutedBatch;
78

@@ -10,12 +11,26 @@ use crate::ExecutedBatch;
1011

1112
/// Executes the batch kernel over a [`ProposedBatch`], producing an [`ExecutedBatch`].
1213
#[derive(Clone, Default)]
13-
pub struct BatchExecutor;
14+
pub struct BatchExecutor {
15+
/// Extra advice inputs merged onto those derived from the proposed batch before execution.
16+
advice_inputs: AdviceInputs,
17+
}
1418

1519
impl BatchExecutor {
1620
/// Creates a new [`BatchExecutor`] instance.
1721
pub fn new() -> Self {
18-
Self
22+
Self::default()
23+
}
24+
25+
/// Extends the advice inputs merged onto those derived from the proposed batch before
26+
/// execution. Entries provided here override matching keys from the derived advice.
27+
///
28+
/// This is primarily a testing hook for exercising the batch kernel's rejection paths by
29+
/// injecting tampered advice, mirroring
30+
/// [`TransactionContextBuilder::extend_advice_inputs`](https://docs.rs/miden-testing).
31+
pub fn extend_advice_inputs(mut self, advice_inputs: AdviceInputs) -> Self {
32+
self.advice_inputs.extend(advice_inputs);
33+
self
1934
}
2035

2136
/// Runs the batch kernel over the [`ProposedBatch`], returning an [`ExecutedBatch`] that can be
@@ -30,7 +45,9 @@ impl BatchExecutor {
3045
&self,
3146
proposed_batch: ProposedBatch,
3247
) -> Result<ExecutedBatch, ProvenBatchError> {
33-
let (stack_inputs, advice_inputs) = BatchKernel::prepare_inputs(&proposed_batch);
48+
let (stack_inputs, mut advice_inputs) = BatchKernel::prepare_inputs(&proposed_batch);
49+
// Merge any caller-provided advice, overriding matching keys from the derived advice.
50+
advice_inputs.extend(self.advice_inputs.clone());
3451

3552
let processor = FastProcessor::new_with_options(
3653
stack_inputs,

0 commit comments

Comments
 (0)