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

Add op batch flags to decoder trace #246

Merged
merged 3 commits into from
Jun 21, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
50 changes: 37 additions & 13 deletions core/src/decoder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{range, Operation};
use super::{range, Felt, Operation, ONE, ZERO};
use core::ops::Range;

// CONSTANTS
Expand All @@ -8,27 +8,51 @@ use core::ops::Range;
pub const ADDR_COL_IDX: usize = 0;

/// Index at which operation bit columns start in the decoder trace.
pub const OP_BITS_OFFSET: usize = 1;
pub const OP_BITS_OFFSET: usize = ADDR_COL_IDX + 1;

/// Location of operation bits columns in the decoder trace.
pub const OP_BITS_RANGE: Range<usize> = range(OP_BITS_OFFSET, Operation::OP_BITS);

/// Index of the in_span column in the decoder trace.
pub const IN_SPAN_COL_IDX: usize = 8;

/// Index of the operation group count column in the decoder trace.
pub const GROUP_COUNT_COL_IDX: usize = 17;
/// Number of columns needed to hold a binary representation of opcodes.
pub const NUM_OP_BITS: usize = Operation::OP_BITS;

/// Index of the operation index column in the decoder trace.
pub const OP_INDEX_COL_IDX: usize = 18;
/// Location of operation bits columns in the decoder trace.
pub const OP_BITS_RANGE: Range<usize> = range(OP_BITS_OFFSET, NUM_OP_BITS);

// TODO: probably rename "hasher state" to something like "shared columns".

/// Index at which hasher state columns start in the decoder trace.
pub const HASHER_STATE_OFFSET: usize = 9;
pub const HASHER_STATE_OFFSET: usize = OP_BITS_OFFSET + NUM_OP_BITS;

/// Number of hasher columns in the decoder trace.
pub const NUM_HASHER_COLUMNS: usize = 8;

/// Location of hasher columns in the decoder trace.
pub const HASHER_STATE_RANGE: Range<usize> = range(HASHER_STATE_OFFSET, NUM_HASHER_COLUMNS);

/// Index of the in_span column in the decoder trace.
pub const IN_SPAN_COL_IDX: usize = HASHER_STATE_OFFSET + NUM_HASHER_COLUMNS;

/// Index of the operation group count column in the decoder trace.
pub const GROUP_COUNT_COL_IDX: usize = IN_SPAN_COL_IDX + 1;

/// Index of the operation index column in the decoder trace.
pub const OP_INDEX_COL_IDX: usize = GROUP_COUNT_COL_IDX + 1;

/// Index at which operation batch flag columns start in the decoder trace.
pub const OP_BATCH_FLAGS_OFFSET: usize = OP_INDEX_COL_IDX + 1;

/// Number of operation batch flag columns.
pub const NUM_OP_BATCH_FLAGS: usize = 3;

/// Location of operation batch flag columns in the decoder trace.
pub const OP_BATCH_FLAGS_RANGE: Range<usize> = range(OP_BATCH_FLAGS_OFFSET, NUM_OP_BATCH_FLAGS);

/// Operation batch consists of 8 operation groups.
pub const OP_BATCH_8_GROUPS: [Felt; NUM_OP_BATCH_FLAGS] = [ONE, ZERO, ZERO];

/// Operation batch consists of 4 operation groups.
pub const OP_BATCH_4_GROUPS: [Felt; NUM_OP_BATCH_FLAGS] = [ZERO, ONE, ZERO];

/// Operation batch consists of 2 operation groups.
pub const OP_BATCH_2_GROUPS: [Felt; NUM_OP_BATCH_FLAGS] = [ZERO, ZERO, ONE];

/// Operation batch consists of 1 operation group.
pub const OP_BATCH_1_GROUPS: [Felt; NUM_OP_BATCH_FLAGS] = [ZERO, ONE, ONE];
10 changes: 8 additions & 2 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ pub type StackTopState = [Felt; MIN_STACK_DEPTH];
// CONSTANTS
// ================================================================================================

/// Field element representing ZERO in the base field of the VM.
pub const ZERO: Felt = Felt::ZERO;

/// Field element representing ONE in the base field of the VM.
pub const ONE: Felt = Felt::ONE;

/// The minimum length of the execution trace. This is the minimum required to support range checks.
pub const MIN_TRACE_LEN: usize = 1024;

Expand All @@ -48,7 +54,7 @@ pub const NUM_STACK_HELPER_COLS: usize = 3;
// ------------------------------------------------------------------------------------------------

// system decoder stack range checks auxiliary table
// (2 columns) (19 columns) (19 columns) (4 columns) (18 columns)
// (2 columns) (22 columns) (19 columns) (4 columns) (18 columns)
// ├───────────────┴───────────────┴───────────────┴───────────────┴─────────────────┤

pub const SYS_TRACE_OFFSET: usize = 0;
Expand All @@ -60,7 +66,7 @@ pub const FMP_COL_IDX: usize = SYS_TRACE_OFFSET + 1;

// decoder trace
pub const DECODER_TRACE_OFFSET: usize = SYS_TRACE_OFFSET + SYS_TRACE_WIDTH;
pub const DECODER_TRACE_WIDTH: usize = 19;
pub const DECODER_TRACE_WIDTH: usize = 22;
pub const DECODER_TRACE_RANGE: Range<usize> = range(DECODER_TRACE_OFFSET, DECODER_TRACE_WIDTH);

// Stack trace
Expand Down
5 changes: 4 additions & 1 deletion core/src/program/blocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ pub use call_block::Call;
pub use join_block::Join;
pub use loop_block::Loop;
pub use proxy_block::Proxy;
pub use span_block::{OpBatch, Span, BATCH_SIZE as OP_BATCH_SIZE, GROUP_SIZE as OP_GROUP_SIZE};
pub use span_block::{
get_span_op_group_count, OpBatch, Span, BATCH_SIZE as OP_BATCH_SIZE,
GROUP_SIZE as OP_GROUP_SIZE,
};
pub use split_block::Split;

// PROGRAM BLOCK
Expand Down
59 changes: 40 additions & 19 deletions core/src/program/blocks/span_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,6 @@ impl Span {
&self.op_batches
}

/// Returns a list of operations contained in this span block.
pub fn get_ops(&self) -> Vec<Operation> {
let mut ops = Vec::with_capacity(self.op_batches.len() * MAX_OPS_PER_BATCH);
for batch in self.op_batches.iter() {
ops.extend_from_slice(&batch.ops);
}
ops
}

// SPAN MUTATORS
// --------------------------------------------------------------------------------------------

Expand All @@ -91,6 +82,18 @@ impl Span {
}
Self::new(ops)
}

// HELPER METHODS
// --------------------------------------------------------------------------------------------

/// Returns a list of operations contained in this span block.
fn get_ops(&self) -> Vec<Operation> {
let mut ops = Vec::with_capacity(self.op_batches.len() * MAX_OPS_PER_BATCH);
for batch in self.op_batches.iter() {
ops.extend_from_slice(&batch.ops);
}
ops
}
}

impl fmt::Display for Span {
Expand Down Expand Up @@ -324,11 +327,29 @@ fn batch_ops(ops: Vec<Operation>) -> (Vec<OpBatch>, Digest) {
batches.push(batch);
}

let hash = hasher::hash_elements(flatten_slice_elements(&batch_groups));
// compute the hash of all operation groups
let num_op_groups = get_span_op_group_count(&batches);
let op_groups = &flatten_slice_elements(&batch_groups)[..num_op_groups];
let hash = hasher::hash_elements(op_groups);

(batches, hash)
}

/// Returns the total number of operation groups in a span defined by the provides list of
/// operation batches.
///
/// Then number of operation groups is computed as follows:
/// - For all batches but the last one we set the number of groups to 8, regardless of the
/// actual number of groups in the batch. The reason for this is that when operation
/// batches are concatenated together each batch contributes 8 elements to the hash.
/// - For the last batch, we take the number of actual batches and round it up to the next
/// power of two. The reason for rounding is that the VM always executes a number of
/// operation groups which is a power of two.
pub fn get_span_op_group_count(op_batches: &[OpBatch]) -> usize {
let last_batch_num_groups = op_batches.last().expect("no last group").num_groups();
(op_batches.len() - 1) * BATCH_SIZE + last_batch_num_groups.next_power_of_two()
}

// TESTS
// ================================================================================================

Expand All @@ -353,7 +374,7 @@ mod tests {

assert_eq!(batch_groups, batch.groups);
assert_eq!([1_usize, 0, 0, 0, 0, 0, 0, 0], batch.op_counts);
assert_eq!(hasher::hash_elements(&batch_groups), hash);
assert_eq!(hasher::hash_elements(&batch_groups[..1]), hash);

// --- two operations ---------------------------------------------------------------------
let ops = vec![Operation::Add, Operation::Mul];
Expand All @@ -369,7 +390,7 @@ mod tests {

assert_eq!(batch_groups, batch.groups);
assert_eq!([2_usize, 0, 0, 0, 0, 0, 0, 0], batch.op_counts);
assert_eq!(hasher::hash_elements(&batch_groups), hash);
assert_eq!(hasher::hash_elements(&batch_groups[..1]), hash);

// --- one group with one immediate value -------------------------------------------------
let ops = vec![Operation::Add, Operation::Push(Felt::new(12345678))];
Expand All @@ -386,7 +407,7 @@ mod tests {

assert_eq!(batch_groups, batch.groups);
assert_eq!([2_usize, 0, 0, 0, 0, 0, 0, 0], batch.op_counts);
assert_eq!(hasher::hash_elements(&batch_groups), hash);
assert_eq!(hasher::hash_elements(&batch_groups[..2]), hash);

// --- one group with 7 immediate values --------------------------------------------------
let ops = vec![
Expand Down Expand Up @@ -467,7 +488,7 @@ mod tests {
assert_eq!(batch1_groups, batch1.groups);

let all_groups = [batch0_groups, batch1_groups].concat();
assert_eq!(hasher::hash_elements(&all_groups), hash);
assert_eq!(hasher::hash_elements(&all_groups[..10]), hash);

// --- immediate values in-between groups -------------------------------------------------
let ops = vec![
Expand Down Expand Up @@ -503,7 +524,7 @@ mod tests {

assert_eq!([9_usize, 0, 0, 1, 0, 0, 0, 0], batch.op_counts);
assert_eq!(batch_groups, batch.groups);
assert_eq!(hasher::hash_elements(&batch_groups), hash);
assert_eq!(hasher::hash_elements(&batch_groups[..4]), hash);

// --- push at the end of a group is moved into the next group ----------------------------
let ops = vec![
Expand Down Expand Up @@ -537,7 +558,7 @@ mod tests {

assert_eq!(batch_groups, batch.groups);
assert_eq!([8_usize, 1, 0, 0, 0, 0, 0, 0], batch.op_counts);
assert_eq!(hasher::hash_elements(&batch_groups), hash);
assert_eq!(hasher::hash_elements(&batch_groups[..4]), hash);

// --- push at the end of a group is moved into the next group ----------------------------
let ops = vec![
Expand Down Expand Up @@ -571,7 +592,7 @@ mod tests {

assert_eq!(batch_groups, batch.groups);
assert_eq!([8_usize, 0, 1, 0, 0, 0, 0, 0], batch.op_counts);
assert_eq!(hasher::hash_elements(&batch_groups), hash);
assert_eq!(hasher::hash_elements(&batch_groups[..4]), hash);

// --- push at the end of the 7th group overflows to the next batch -----------------------
let ops = vec![
Expand Down Expand Up @@ -635,7 +656,7 @@ mod tests {
assert_eq!([2_usize, 0, 0, 0, 0, 0, 0, 0], batch1.op_counts);

let all_groups = [batch0_groups, batch1_groups].concat();
assert_eq!(hasher::hash_elements(&all_groups), hash);
assert_eq!(hasher::hash_elements(&all_groups[..10]), hash);
}

#[test]
Expand All @@ -654,7 +675,7 @@ mod tests {
batch_groups[0] = build_group(&ops);
batch_groups[1] = Felt::ONE;
assert_eq!(batch_groups, batch.groups);
assert_eq!(hasher::hash_elements(&batch_groups), hash);
assert_eq!(hasher::hash_elements(&batch_groups[..2]), hash);
}

// TEST HELPERS
Expand Down
8 changes: 3 additions & 5 deletions core/src/program/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,14 @@ pub use library::Library;
#[derive(Clone, Debug)]
pub struct Script {
root: CodeBlock,
hash: Digest,
}

impl Script {
// CONSTRUCTOR
// --------------------------------------------------------------------------------------------
/// Constructs a new program from the specified code block.
pub fn new(root: CodeBlock) -> Self {
let hash = hasher::merge(&[root.hash(), Digest::default()]);
Self { root, hash }
Self { root }
}

// PUBLIC ACCESSORS
Expand All @@ -46,8 +44,8 @@ impl Script {
}

/// Returns a hash of this script.
pub fn hash(&self) -> &Digest {
&self.hash
pub fn hash(&self) -> Digest {
self.root.hash()
}
}

Expand Down
4 changes: 2 additions & 2 deletions examples/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ pub fn test_example(example: Example, fail: bool) {

if fail {
outputs[0] += 1;
assert!(miden::verify(*program.hash(), &pub_inputs, &outputs, proof).is_err())
assert!(miden::verify(program.hash(), &pub_inputs, &outputs, proof).is_err())
} else {
assert!(miden::verify(*program.hash(), &pub_inputs, &outputs, proof).is_ok());
assert!(miden::verify(program.hash(), &pub_inputs, &outputs, proof).is_ok());
}
}
2 changes: 1 addition & 1 deletion examples/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ fn main() {
// results in the expected output
let proof = StarkProof::from_bytes(&proof_bytes).unwrap();
let now = Instant::now();
match miden::verify(*program.hash(), &pub_inputs, &outputs, proof) {
match miden::verify(program.hash(), &pub_inputs, &outputs, proof) {
Ok(_) => debug!("Execution verified in {} ms", now.elapsed().as_millis()),
Err(err) => debug!("Failed to verify execution: {}", err),
}
Expand Down
4 changes: 2 additions & 2 deletions miden/tests/integration/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@ impl Test {

if test_fail {
outputs[0] += 1;
assert!(miden::verify(*script.hash(), &pub_inputs, &outputs, proof).is_err());
assert!(miden::verify(script.hash(), &pub_inputs, &outputs, proof).is_err());
} else {
assert!(miden::verify(*script.hash(), &pub_inputs, &outputs, proof).is_ok());
assert!(miden::verify(script.hash(), &pub_inputs, &outputs, proof).is_ok());
}
}

Expand Down
Loading