Skip to content

Commit

Permalink
Merge pull request #246 from maticnetwork/decoder-trace2
Browse files Browse the repository at this point in the history
Add op batch flags to decoder trace
  • Loading branch information
bobbinth committed Jun 21, 2022
2 parents 1f07ac9 + e12b6fb commit 923730f
Show file tree
Hide file tree
Showing 15 changed files with 390 additions and 195 deletions.
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

0 comments on commit 923730f

Please sign in to comment.