diff --git a/CHANGELOG.md b/CHANGELOG.md index e3f48ae8f3..71c22809b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Update minimum supported Rust version to 1.84. - Change Chiplet Fields to Public (#1629). - Added to the `Assembler` the ability to vendor a compiled library. +- Introduce `BusDebugger` to facilitate debugging buses (#1664) ## 0.12.0 (2025-01-22) diff --git a/Cargo.lock b/Cargo.lock index aaa5b5b9f7..dd674229d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -697,6 +697,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -709,6 +720,12 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.31" @@ -716,10 +733,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-macro", "futures-sink", "futures-task", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -1190,6 +1209,7 @@ dependencies = [ "miden-assembly", "miden-core", "miden-test-utils", + "rstest", "thiserror 2.0.11", "tracing", "winter-fri", @@ -1610,6 +1630,15 @@ dependencies = [ "yansi", ] +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.93" @@ -1788,6 +1817,42 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + +[[package]] +name = "rstest" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e905296805ab93e13c1ec3a03f4b6c4f35e9498a3d5fa96dc626d22c03cd89" +dependencies = [ + "futures-timer", + "futures-util", + "rstest_macros", + "rustc_version 0.4.1", +] + +[[package]] +name = "rstest_macros" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef0053bbffce09062bee4bcc499b0fbe7a57b879f1efe088d6d8d4c7adcdef9b" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version 0.4.1", + "syn", + "unicode-ident", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1993,6 +2058,15 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.13.2" diff --git a/processor/Cargo.toml b/processor/Cargo.toml index 46c04d313b..0069c7642a 100644 --- a/processor/Cargo.toml +++ b/processor/Cargo.toml @@ -22,6 +22,8 @@ concurrent = ["std", "winter-prover/concurrent"] default = ["std"] std = ["vm-core/std", "winter-prover/std", "thiserror/std"] testing = ["miden-air/testing"] +# Like `testing`, but slows down the processor speed to make it easier to debug. +bus-debugger = ["testing", "miden-air/testing"] [dependencies] miden-air = { package = "miden-air", path = "../air", version = "0.13", default-features = false } @@ -33,6 +35,7 @@ thiserror = { workspace = true } [dev-dependencies] assembly = { package = "miden-assembly", path = "../assembly", version = "0.13", default-features = false } logtest = { version = "2.0", default-features = false } +rstest = { version = "0.24" } test-utils = { package = "miden-test-utils", path = "../test-utils" } winter-fri = { package = "winter-fri", version = "0.11" } winter-utils = { package = "winter-utils", version = "0.11" } diff --git a/processor/src/chiplets/aux_trace/bus/bitwise.rs b/processor/src/chiplets/aux_trace/bus/bitwise.rs new file mode 100644 index 0000000000..feeaae58e3 --- /dev/null +++ b/processor/src/chiplets/aux_trace/bus/bitwise.rs @@ -0,0 +1,106 @@ +use core::fmt::{Display, Formatter, Result as FmtResult}; + +use miden_air::{ + trace::{chiplets::bitwise::OP_CYCLE_LEN as BITWISE_OP_CYCLE_LEN, main_trace::MainTrace}, + RowIndex, +}; +use vm_core::{Felt, FieldElement, ONE, ZERO}; + +use super::{build_value, get_op_label}; +use crate::debug::{BusDebugger, BusMessage}; + +// REQUESTS +// ============================================================================================== + +/// Builds requests made to the bitwise chiplet. This can be either a request for the computation +/// of a `XOR` or an `AND` operation. +pub(super) fn build_bitwise_request>( + main_trace: &MainTrace, + is_xor: Felt, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let bitwise_request_message = BitwiseMessage { + op_label: get_op_label(ONE, ZERO, is_xor, ZERO), + a: main_trace.stack_element(1, row), + b: main_trace.stack_element(0, row), + z: main_trace.stack_element(0, row + 1), + source: if is_xor == ONE { "u32xor" } else { "u32and" }, + }; + + let value = bitwise_request_message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_request(alloc::boxed::Box::new(bitwise_request_message), alphas); + + value +} + +// RESPONSES +// ============================================================================================== + +/// Builds the response from the bitwise chiplet at `row`. +pub(super) fn build_bitwise_chiplet_responses( + main_trace: &MainTrace, + row: RowIndex, + alphas: &[E], + _debugger: &mut BusDebugger, +) -> E +where + E: FieldElement, +{ + let is_xor = main_trace.chiplet_selector_2(row); + if row.as_usize() % BITWISE_OP_CYCLE_LEN == BITWISE_OP_CYCLE_LEN - 1 { + let bitwise_message = BitwiseMessage { + op_label: get_op_label(ONE, ZERO, is_xor, ZERO), + a: main_trace.chiplet_bitwise_a(row), + b: main_trace.chiplet_bitwise_b(row), + z: main_trace.chiplet_bitwise_z(row), + source: "bitwise chiplet", + }; + + let value = bitwise_message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_response(alloc::boxed::Box::new(bitwise_message), alphas); + + value + } else { + E::ONE + } +} + +// MESSAGE +// =============================================================================================== + +pub struct BitwiseMessage { + pub op_label: Felt, + pub a: Felt, + pub b: Felt, + pub z: Felt, + pub source: &'static str, +} + +impl BusMessage for BitwiseMessage +where + E: FieldElement, +{ + fn value(&self, alphas: &[E]) -> E { + alphas[0] + build_value(&alphas[1..5], [self.op_label, self.a, self.b, self.z]) + } + + fn source(&self) -> &str { + self.source + } +} + +impl Display for BitwiseMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!( + f, + "{{ op_label: {}, a: {}, b: {}, z: {} }}", + self.op_label, self.a, self.b, self.z + ) + } +} diff --git a/processor/src/chiplets/aux_trace/bus/hasher.rs b/processor/src/chiplets/aux_trace/bus/hasher.rs new file mode 100644 index 0000000000..4f59a70c0b --- /dev/null +++ b/processor/src/chiplets/aux_trace/bus/hasher.rs @@ -0,0 +1,744 @@ +use core::fmt::{Display, Formatter, Result as FmtResult}; + +use miden_air::{ + trace::{ + chiplets::{ + hasher, + hasher::{ + HASH_CYCLE_LEN, LINEAR_HASH_LABEL, MP_VERIFY_LABEL, MR_UPDATE_NEW_LABEL, + MR_UPDATE_OLD_LABEL, NUM_ROUNDS, RETURN_HASH_LABEL, RETURN_STATE_LABEL, + }, + }, + main_trace::MainTrace, + }, + RowIndex, +}; +use vm_core::{ + utils::range, Felt, FieldElement, ONE, OPCODE_CALL, OPCODE_JOIN, OPCODE_LOOP, OPCODE_SPLIT, + ZERO, +}; + +use super::{build_value, get_op_label}; +use crate::debug::{BusDebugger, BusMessage}; +// REQUESTS +// ============================================================================================== + +/// Builds requests made to the hasher chiplet at the start of a control block. +pub(super) fn build_control_block_request>( + main_trace: &MainTrace, + decoder_hasher_state: [Felt; 8], + op_code_felt: Felt, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let message = ControlBlockRequestMessage { + transition_label: Felt::from(LINEAR_HASH_LABEL + 16), + addr_next: main_trace.addr(row + 1), + op_code: op_code_felt, + decoder_hasher_state, + }; + + let value = message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_request(alloc::boxed::Box::new(message), alphas); + + value +} + +/// Builds requests made to the hasher chiplet at the start of a span block. +pub(super) fn build_span_block_request>( + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let span_block_message = SpanBlockMessage { + transition_label: Felt::from(LINEAR_HASH_LABEL + 16), + addr_next: main_trace.addr(row + 1), + state: main_trace.decoder_hasher_state(row), + }; + + let value = span_block_message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_request(alloc::boxed::Box::new(span_block_message), alphas); + + value +} + +/// Builds requests made to the hasher chiplet at the start of a respan block. +pub(super) fn build_respan_block_request>( + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let respan_block_message = RespanBlockMessage { + transition_label: Felt::from(LINEAR_HASH_LABEL + 32), + addr_next: main_trace.addr(row + 1), + state: main_trace.decoder_hasher_state(row), + }; + + let value = respan_block_message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_request(alloc::boxed::Box::new(respan_block_message), alphas); + + value +} + +/// Builds requests made to the hasher chiplet at the end of a block. +pub(super) fn build_end_block_request>( + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let end_block_message = EndBlockMessage { + addr: main_trace.addr(row) + Felt::from(NUM_ROUNDS as u8), + transition_label: Felt::from(RETURN_HASH_LABEL + 32), + digest: main_trace.decoder_hasher_state(row)[..4].try_into().unwrap(), + }; + + let value = end_block_message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_request(alloc::boxed::Box::new(end_block_message), alphas); + + value +} + +/// Builds `HPERM` requests made to the hash chiplet. +pub(super) fn build_hperm_request>( + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let helper_0 = main_trace.helper_register(0, row); + let s0 = main_trace.stack_element(0, row); + let s1 = main_trace.stack_element(1, row); + let s2 = main_trace.stack_element(2, row); + let s3 = main_trace.stack_element(3, row); + let s4 = main_trace.stack_element(4, row); + let s5 = main_trace.stack_element(5, row); + let s6 = main_trace.stack_element(6, row); + let s7 = main_trace.stack_element(7, row); + let s8 = main_trace.stack_element(8, row); + let s9 = main_trace.stack_element(9, row); + let s10 = main_trace.stack_element(10, row); + let s11 = main_trace.stack_element(11, row); + let s0_nxt = main_trace.stack_element(0, row + 1); + let s1_nxt = main_trace.stack_element(1, row + 1); + let s2_nxt = main_trace.stack_element(2, row + 1); + let s3_nxt = main_trace.stack_element(3, row + 1); + let s4_nxt = main_trace.stack_element(4, row + 1); + let s5_nxt = main_trace.stack_element(5, row + 1); + let s6_nxt = main_trace.stack_element(6, row + 1); + let s7_nxt = main_trace.stack_element(7, row + 1); + let s8_nxt = main_trace.stack_element(8, row + 1); + let s9_nxt = main_trace.stack_element(9, row + 1); + let s10_nxt = main_trace.stack_element(10, row + 1); + let s11_nxt = main_trace.stack_element(11, row + 1); + + let input_req = HasherMessage { + transition_label: Felt::from(LINEAR_HASH_LABEL + 16), + addr_next: helper_0, + node_index: ZERO, + hasher_state: [s11, s10, s9, s8, s7, s6, s5, s4, s3, s2, s1, s0], + source: "hperm input", + }; + let output_req = HasherMessage { + transition_label: Felt::from(RETURN_STATE_LABEL + 32), + addr_next: helper_0 + Felt::new(7), + node_index: ZERO, + hasher_state: [ + s11_nxt, s10_nxt, s9_nxt, s8_nxt, s7_nxt, s6_nxt, s5_nxt, s4_nxt, s3_nxt, s2_nxt, + s1_nxt, s0_nxt, + ], + source: "hperm output", + }; + + let combined_value = input_req.value(alphas) * output_req.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + { + _debugger.add_request(alloc::boxed::Box::new(input_req), alphas); + _debugger.add_request(alloc::boxed::Box::new(output_req), alphas); + } + + combined_value +} + +/// Builds `MPVERIFY` requests made to the hash chiplet. +pub(super) fn build_mpverify_request>( + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let helper_0 = main_trace.helper_register(0, row); + + let node_value = [ + main_trace.stack_element(0, row), + main_trace.stack_element(1, row), + main_trace.stack_element(2, row), + main_trace.stack_element(3, row), + ]; + let node_depth = main_trace.stack_element(4, row); + let node_index = main_trace.stack_element(5, row); + + let merkle_tree_root = [ + main_trace.stack_element(6, row), + main_trace.stack_element(7, row), + main_trace.stack_element(8, row), + main_trace.stack_element(9, row), + ]; + + let input = HasherMessage { + transition_label: Felt::from(MP_VERIFY_LABEL + 16), + addr_next: helper_0, + node_index, + hasher_state: [ + ZERO, + ZERO, + ZERO, + ZERO, + node_value[3], + node_value[2], + node_value[1], + node_value[0], + ZERO, + ZERO, + ZERO, + ZERO, + ], + source: "mpverify input", + }; + + let output = HasherMessage { + transition_label: Felt::from(RETURN_HASH_LABEL + 32), + addr_next: helper_0 + node_depth.mul_small(8) - ONE, + node_index: ZERO, + hasher_state: [ + ZERO, + ZERO, + ZERO, + ZERO, + merkle_tree_root[3], + merkle_tree_root[2], + merkle_tree_root[1], + merkle_tree_root[0], + ZERO, + ZERO, + ZERO, + ZERO, + ], + source: "mpverify output", + }; + + let combined_value = input.value(alphas) * output.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + { + _debugger.add_request(alloc::boxed::Box::new(input), alphas); + _debugger.add_request(alloc::boxed::Box::new(output), alphas); + } + + combined_value +} + +/// Builds `MRUPDATE` requests made to the hash chiplet. +pub(super) fn build_mrupdate_request>( + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let helper_0 = main_trace.helper_register(0, row); + + let old_node_value = [ + main_trace.stack_element(0, row), + main_trace.stack_element(1, row), + main_trace.stack_element(2, row), + main_trace.stack_element(3, row), + ]; + let merkle_path_depth = main_trace.stack_element(4, row); + let node_index = main_trace.stack_element(5, row); + let old_root = [ + main_trace.stack_element(6, row), + main_trace.stack_element(7, row), + main_trace.stack_element(8, row), + main_trace.stack_element(9, row), + ]; + let new_node_value = [ + main_trace.stack_element(10, row), + main_trace.stack_element(11, row), + main_trace.stack_element(12, row), + main_trace.stack_element(13, row), + ]; + let new_root = [ + main_trace.stack_element(0, row + 1), + main_trace.stack_element(1, row + 1), + main_trace.stack_element(2, row + 1), + main_trace.stack_element(3, row + 1), + ]; + + let input_old = HasherMessage { + transition_label: Felt::from(MR_UPDATE_OLD_LABEL + 16), + addr_next: helper_0, + node_index, + hasher_state: [ + ZERO, + ZERO, + ZERO, + ZERO, + old_node_value[3], + old_node_value[2], + old_node_value[1], + old_node_value[0], + ZERO, + ZERO, + ZERO, + ZERO, + ], + source: "mrupdate input_old", + }; + + let output_old = HasherMessage { + transition_label: Felt::from(RETURN_HASH_LABEL + 32), + addr_next: helper_0 + merkle_path_depth.mul_small(8) - ONE, + node_index: ZERO, + hasher_state: [ + ZERO, + ZERO, + ZERO, + ZERO, + old_root[3], + old_root[2], + old_root[1], + old_root[0], + ZERO, + ZERO, + ZERO, + ZERO, + ], + source: "mrupdate output_old", + }; + + let input_new = HasherMessage { + transition_label: Felt::from(MR_UPDATE_NEW_LABEL + 16), + addr_next: helper_0 + merkle_path_depth.mul_small(8), + node_index, + hasher_state: [ + ZERO, + ZERO, + ZERO, + ZERO, + new_node_value[3], + new_node_value[2], + new_node_value[1], + new_node_value[0], + ZERO, + ZERO, + ZERO, + ZERO, + ], + source: "mrupdate input_new", + }; + + let output_new = HasherMessage { + transition_label: Felt::from(RETURN_HASH_LABEL + 32), + addr_next: helper_0 + merkle_path_depth.mul_small(16) - ONE, + node_index: ZERO, + hasher_state: [ + ZERO, + ZERO, + ZERO, + ZERO, + new_root[3], + new_root[2], + new_root[1], + new_root[0], + ZERO, + ZERO, + ZERO, + ZERO, + ], + source: "mrupdate output_new", + }; + + let combined_value = input_old.value(alphas) + * output_old.value(alphas) + * input_new.value(alphas) + * output_new.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + { + _debugger.add_request(alloc::boxed::Box::new(input_old), alphas); + _debugger.add_request(alloc::boxed::Box::new(output_old), alphas); + _debugger.add_request(alloc::boxed::Box::new(input_new), alphas); + _debugger.add_request(alloc::boxed::Box::new(output_new), alphas); + } + + combined_value +} + +// RESPONSES +// ============================================================================================== + +/// Builds the response from the hasher chiplet at `row`. +pub(super) fn build_hasher_chiplet_responses( + main_trace: &MainTrace, + row: RowIndex, + alphas: &[E], + _debugger: &mut BusDebugger, +) -> E +where + E: FieldElement, +{ + let mut multiplicand = E::ONE; + let selector0 = main_trace.chiplet_selector_0(row); + let selector1 = main_trace.chiplet_selector_1(row); + let selector2 = main_trace.chiplet_selector_2(row); + let selector3 = main_trace.chiplet_selector_3(row); + let op_label = get_op_label(selector0, selector1, selector2, selector3); + let addr_next = Felt::from(row + 1); + + // f_bp, f_mp, f_mv or f_mu == 1 + if row.as_usize() % HASH_CYCLE_LEN == 0 { + let state = main_trace.chiplet_hasher_state(row); + let node_index = main_trace.chiplet_node_index(row); + let transition_label = op_label + Felt::from(16_u8); + + // f_bp == 1 + // v_all = v_h + v_a + v_b + v_c + if selector1 == ONE && selector2 == ZERO && selector3 == ZERO { + let hasher_message = HasherMessage { + transition_label, + addr_next, + node_index, + hasher_state: state, + source: "hasher", + }; + multiplicand = hasher_message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_response(alloc::boxed::Box::new(hasher_message), alphas); + } + + // f_mp or f_mv or f_mu == 1 + // v_leaf = v_h + (1 - b) * v_b + b * v_d + if selector1 == ONE && !(selector2 == ZERO && selector3 == ZERO) { + let bit = (node_index.as_int() & 1) as u8; + if bit == 0 { + let hasher_message = HasherMessage { + transition_label, + addr_next, + node_index, + hasher_state: [ + ZERO, ZERO, ZERO, ZERO, state[4], state[5], state[6], state[7], ZERO, ZERO, + ZERO, ZERO, + ], + source: "hasher", + }; + + multiplicand = hasher_message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_response(alloc::boxed::Box::new(hasher_message), alphas); + } else { + let hasher_message = HasherMessage { + transition_label, + addr_next, + node_index, + hasher_state: [ + ZERO, ZERO, ZERO, ZERO, state[8], state[9], state[10], state[11], ZERO, + ZERO, ZERO, ZERO, + ], + source: "hasher", + }; + + multiplicand = hasher_message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_response(alloc::boxed::Box::new(hasher_message), alphas); + } + } + } + + // f_hout, f_sout, f_abp == 1 + if row.as_usize() % HASH_CYCLE_LEN == HASH_CYCLE_LEN - 1 { + let state = main_trace.chiplet_hasher_state(row); + let node_index = main_trace.chiplet_node_index(row); + let transition_label = op_label + Felt::from(32_u8); + + // f_hout == 1 + // v_res = v_h + v_b; + if selector1 == ZERO && selector2 == ZERO && selector3 == ZERO { + let hasher_message = HasherMessage { + transition_label, + addr_next, + node_index, + hasher_state: [ + ZERO, ZERO, ZERO, ZERO, state[4], state[5], state[6], state[7], ZERO, ZERO, + ZERO, ZERO, + ], + source: "hasher", + }; + multiplicand = hasher_message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_response(alloc::boxed::Box::new(hasher_message), alphas); + } + + // f_sout == 1 + // v_all = v_h + v_a + v_b + v_c + if selector1 == ZERO && selector2 == ZERO && selector3 == ONE { + let hasher_message = HasherMessage { + transition_label, + addr_next, + node_index, + hasher_state: state, + source: "hasher", + }; + + multiplicand = hasher_message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_response(alloc::boxed::Box::new(hasher_message), alphas); + } + + // f_abp == 1 + // v_abp = v_h + v_b' + v_c' - v_b - v_c + if selector1 == ONE && selector2 == ZERO && selector3 == ZERO { + // build the value from the hasher state's just right after the absorption of new + // elements. + let state_nxt = main_trace.chiplet_hasher_state(row + 1); + + let hasher_message = HasherMessage { + transition_label, + addr_next, + node_index, + hasher_state: [ + ZERO, + ZERO, + ZERO, + ZERO, + state_nxt[4], + state_nxt[5], + state_nxt[6], + state_nxt[7], + state_nxt[8], + state_nxt[9], + state_nxt[10], + state_nxt[11], + ], + source: "hasher", + }; + + multiplicand = hasher_message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_response(alloc::boxed::Box::new(hasher_message), alphas); + } + } + multiplicand +} + +// CONTROL BLOCK REQUEST MESSAGE +// =============================================================================================== + +pub struct ControlBlockRequestMessage { + pub transition_label: Felt, + pub addr_next: Felt, + pub op_code: Felt, + pub decoder_hasher_state: [Felt; 8], +} + +impl BusMessage for ControlBlockRequestMessage +where + E: FieldElement, +{ + fn value(&self, alphas: &[E]) -> E { + let header = alphas[0] + + alphas[1].mul_base(self.transition_label) + + alphas[2].mul_base(self.addr_next); + + header + + alphas[5].mul_base(self.op_code) + + build_value(&alphas[8..16], self.decoder_hasher_state) + } + + fn source(&self) -> &str { + let op_code = self.op_code.as_int() as u8; + match op_code { + OPCODE_JOIN => "join", + OPCODE_SPLIT => "split", + OPCODE_LOOP => "loop", + OPCODE_CALL => "call", + _ => panic!("unexpected opcode: {op_code}"), + } + } +} + +impl Display for ControlBlockRequestMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!( + f, + "{{ transition_label: {}, addr_next: {}, op_code: {}, decoder_hasher_state: {:?} }}", + self.transition_label, self.addr_next, self.op_code, self.decoder_hasher_state + ) + } +} + +// GENERIC HASHER MESSAGE +// =============================================================================================== + +const NUM_HEADER_ALPHAS: usize = 4; + +pub struct HasherMessage { + pub transition_label: Felt, + pub addr_next: Felt, + pub node_index: Felt, + pub hasher_state: [Felt; hasher::STATE_WIDTH], + pub source: &'static str, +} + +impl BusMessage for HasherMessage +where + E: FieldElement, +{ + fn value(&self, alphas: &[E]) -> E { + let header = alphas[0] + + alphas[1].mul_base(self.transition_label) + + alphas[2].mul_base(self.addr_next) + + alphas[3].mul_base(self.node_index); + + header + + build_value(&alphas[range(NUM_HEADER_ALPHAS, hasher::STATE_WIDTH)], self.hasher_state) + } + + fn source(&self) -> &str { + self.source + } +} + +impl Display for HasherMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!( + f, + "{{ transition_label: {}, addr_next: {}, node_index: {}, decoder_hasher_state: {:?} }}", + self.transition_label, self.addr_next, self.node_index, self.hasher_state + ) + } +} + +// SPAN BLOCK MESSAGE +// =============================================================================================== + +pub struct SpanBlockMessage { + pub transition_label: Felt, + pub addr_next: Felt, + pub state: [Felt; 8], +} + +impl BusMessage for SpanBlockMessage +where + E: FieldElement, +{ + fn value(&self, alphas: &[E]) -> E { + let header = alphas[0] + + alphas[1].mul_base(self.transition_label) + + alphas[2].mul_base(self.addr_next); + + header + build_value(&alphas[8..16], self.state) + } + + fn source(&self) -> &str { + "span" + } +} + +impl Display for SpanBlockMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!( + f, + "{{ transition_label: {}, addr_next: {}, state: {:?} }}", + self.transition_label, self.addr_next, self.state + ) + } +} + +// RESPAN BLOCK MESSAGE +// =============================================================================================== + +pub struct RespanBlockMessage { + pub transition_label: Felt, + pub addr_next: Felt, + pub state: [Felt; 8], +} + +impl BusMessage for RespanBlockMessage +where + E: FieldElement, +{ + fn value(&self, alphas: &[E]) -> E { + let header = alphas[0] + + alphas[1].mul_base(self.transition_label) + + alphas[2].mul_base(self.addr_next - ONE); + + header + build_value(&alphas[8..16], self.state) + } + + fn source(&self) -> &str { + "respan" + } +} + +impl Display for RespanBlockMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!( + f, + "{{ transition_label: {}, addr_next: {}, state: {:?} }}", + self.transition_label, self.addr_next, self.state + ) + } +} + +// END BLOCK MESSAGE +// =============================================================================================== + +pub struct EndBlockMessage { + pub addr: Felt, + pub transition_label: Felt, + pub digest: [Felt; 4], +} + +impl BusMessage for EndBlockMessage +where + E: FieldElement, +{ + fn value(&self, alphas: &[E]) -> E { + let header = + alphas[0] + alphas[1].mul_base(self.transition_label) + alphas[2].mul_base(self.addr); + + header + build_value(&alphas[8..12], self.digest) + } + + fn source(&self) -> &str { + "end" + } +} + +impl Display for EndBlockMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!( + f, + "{{ addr: {}, transition_label: {}, digest: {:?} }}", + self.addr, self.transition_label, self.digest + ) + } +} diff --git a/processor/src/chiplets/aux_trace/bus/kernel.rs b/processor/src/chiplets/aux_trace/bus/kernel.rs new file mode 100644 index 0000000000..26eca183d3 --- /dev/null +++ b/processor/src/chiplets/aux_trace/bus/kernel.rs @@ -0,0 +1,83 @@ +use core::fmt::{Display, Formatter, Result as FmtResult}; + +use miden_air::{ + trace::{chiplets::kernel_rom::KERNEL_PROC_LABEL, main_trace::MainTrace}, + RowIndex, +}; +use vm_core::{Felt, FieldElement, ONE}; + +use crate::debug::{BusDebugger, BusMessage}; + +// REQUESTS +// ================================================================================================ + +// Note: all requests are handled in the `super` module, since they involve messages to multiple +// chiplets. + +// RESPONSES +// ================================================================================================ + +/// Builds the response from the kernel chiplet at `row`. +pub(super) fn build_kernel_chiplet_responses( + main_trace: &MainTrace, + row: RowIndex, + alphas: &[E], + _debugger: &mut BusDebugger, +) -> E +where + E: FieldElement, +{ + let kernel_chiplet_selector = main_trace.chiplet_selector_4(row); + if kernel_chiplet_selector == ONE { + let message = { + let root0 = main_trace.chiplet_kernel_root_0(row); + let root1 = main_trace.chiplet_kernel_root_1(row); + let root2 = main_trace.chiplet_kernel_root_2(row); + let root3 = main_trace.chiplet_kernel_root_3(row); + + KernelRomMessage { + kernel_proc_digest: [root0, root1, root2, root3], + } + }; + + let value = message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_response(alloc::boxed::Box::new(message), alphas); + + value + } else { + E::ONE + } +} + +// MESSAGE +// =============================================================================================== + +pub struct KernelRomMessage { + pub kernel_proc_digest: [Felt; 4], +} + +impl BusMessage for KernelRomMessage +where + E: FieldElement, +{ + fn value(&self, alphas: &[E]) -> E { + alphas[0] + + alphas[1].mul_base(KERNEL_PROC_LABEL) + + alphas[2].mul_base(self.kernel_proc_digest[0]) + + alphas[3].mul_base(self.kernel_proc_digest[1]) + + alphas[4].mul_base(self.kernel_proc_digest[2]) + + alphas[5].mul_base(self.kernel_proc_digest[3]) + } + + fn source(&self) -> &str { + "kernel rom" + } +} + +impl Display for KernelRomMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!(f, "{{ proc digest: {:?} }}", self.kernel_proc_digest) + } +} diff --git a/processor/src/chiplets/aux_trace/bus/memory.rs b/processor/src/chiplets/aux_trace/bus/memory.rs new file mode 100644 index 0000000000..9c0b7759a3 --- /dev/null +++ b/processor/src/chiplets/aux_trace/bus/memory.rs @@ -0,0 +1,420 @@ +use alloc::boxed::Box; +use core::fmt::{Display, Formatter, Result as FmtResult}; + +use miden_air::{ + trace::{ + chiplets::memory::{ + MEMORY_ACCESS_ELEMENT, MEMORY_ACCESS_WORD, MEMORY_READ_ELEMENT_LABEL, + MEMORY_READ_WORD_LABEL, MEMORY_WRITE_ELEMENT_LABEL, MEMORY_WRITE_WORD_LABEL, + }, + main_trace::MainTrace, + }, + RowIndex, +}; +use vm_core::{Felt, FieldElement, ONE, ZERO}; + +use super::build_value; +use crate::debug::{BusDebugger, BusMessage}; + +// CONSTANTS +// ================================================================================================ + +const FOUR: Felt = Felt::new(4); + +// REQUESTS +// ================================================================================================ + +/// Builds `MLOADW` and `MSTOREW` requests made to the memory chiplet. +pub(super) fn build_mem_mloadw_mstorew_request>( + main_trace: &MainTrace, + op_label: u8, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let word = [ + main_trace.stack_element(3, row + 1), + main_trace.stack_element(2, row + 1), + main_trace.stack_element(1, row + 1), + main_trace.stack_element(0, row + 1), + ]; + let addr = main_trace.stack_element(0, row); + + debug_assert!(op_label == MEMORY_READ_WORD_LABEL || op_label == MEMORY_WRITE_WORD_LABEL); + let ctx = main_trace.ctx(row); + let clk = main_trace.clk(row); + + let message = MemoryWordMessage { + op_label: Felt::from(op_label), + ctx, + addr, + clk, + word, + source: if op_label == MEMORY_READ_WORD_LABEL { + "mloadw" + } else { + "mstorew" + }, + }; + + let value = message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_request(Box::new(message), alphas); + + value +} + +/// Builds `MLOAD` and `MSTORE` requests made to the memory chiplet. +pub(super) fn build_mem_mload_mstore_request>( + main_trace: &MainTrace, + op_label: u8, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let element = main_trace.stack_element(0, row + 1); + let addr = main_trace.stack_element(0, row); + + debug_assert!(op_label == MEMORY_READ_ELEMENT_LABEL || op_label == MEMORY_WRITE_ELEMENT_LABEL); + + let ctx = main_trace.ctx(row); + let clk = main_trace.clk(row); + + let message = MemoryElementMessage { + op_label: Felt::from(op_label), + ctx, + addr, + clk, + element, + }; + + let value = message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_request(Box::new(message), alphas); + + value +} + +/// Builds `MSTREAM` requests made to the memory chiplet. +pub(super) fn build_mstream_request>( + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let op_label = Felt::from(MEMORY_READ_WORD_LABEL); + let addr = main_trace.stack_element(12, row); + let ctx = main_trace.ctx(row); + let clk = main_trace.clk(row); + + let mem_req_1 = MemoryWordMessage { + op_label, + ctx, + addr, + clk, + word: [ + main_trace.stack_element(7, row + 1), + main_trace.stack_element(6, row + 1), + main_trace.stack_element(5, row + 1), + main_trace.stack_element(4, row + 1), + ], + source: "mstream req 1", + }; + let mem_req_2 = MemoryWordMessage { + op_label, + ctx, + addr: addr + FOUR, + clk, + word: [ + main_trace.stack_element(3, row + 1), + main_trace.stack_element(2, row + 1), + main_trace.stack_element(1, row + 1), + main_trace.stack_element(0, row + 1), + ], + source: "mstream req 2", + }; + + let combined_value = mem_req_1.value(alphas) * mem_req_2.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + { + _debugger.add_request(Box::new(mem_req_1), alphas); + _debugger.add_request(Box::new(mem_req_2), alphas); + } + + combined_value +} + +/// Builds `PIPE` requests made to the memory chiplet. +pub(super) fn build_pipe_request>( + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let op_label = Felt::from(MEMORY_WRITE_WORD_LABEL); + let addr = main_trace.stack_element(12, row); + let ctx = main_trace.ctx(row); + let clk = main_trace.clk(row); + + let mem_req_1 = MemoryWordMessage { + op_label, + ctx, + addr, + clk, + word: [ + main_trace.stack_element(7, row + 1), + main_trace.stack_element(6, row + 1), + main_trace.stack_element(5, row + 1), + main_trace.stack_element(4, row + 1), + ], + source: "pipe req 1", + }; + let mem_req_2 = MemoryWordMessage { + op_label, + ctx, + addr: addr + FOUR, + clk, + word: [ + main_trace.stack_element(3, row + 1), + main_trace.stack_element(2, row + 1), + main_trace.stack_element(1, row + 1), + main_trace.stack_element(0, row + 1), + ], + source: "pipe req 2", + }; + + let combined_value = mem_req_1.value(alphas) * mem_req_2.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + { + _debugger.add_request(Box::new(mem_req_1), alphas); + _debugger.add_request(Box::new(mem_req_2), alphas); + } + + combined_value +} + +/// Builds `RCOMBBASE` requests made to the memory chiplet. +pub(super) fn build_rcomb_base_request>( + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let tz0 = main_trace.helper_register(0, row); + let tz1 = main_trace.helper_register(1, row); + let tzg0 = main_trace.helper_register(2, row); + let tzg1 = main_trace.helper_register(3, row); + let a0 = main_trace.helper_register(4, row); + let a1 = main_trace.helper_register(5, row); + let z_ptr = main_trace.stack_element(13, row); + let a_ptr = main_trace.stack_element(14, row); + let op_label = Felt::from(MEMORY_READ_WORD_LABEL); + + let ctx = main_trace.ctx(row); + let clk = main_trace.clk(row); + + let mem_req_1 = MemoryWordMessage { + op_label, + ctx, + addr: z_ptr, + clk, + word: [tz0, tz1, tzg0, tzg1], + source: "rcombbase req 1", + }; + let mem_req_2 = MemoryWordMessage { + op_label, + ctx, + addr: a_ptr, + clk, + word: [a0, a1, ZERO, ZERO], + source: "rcombbase req 2", + }; + + let combined_value = mem_req_1.value(alphas) * mem_req_2.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + { + _debugger.add_request(Box::new(mem_req_1), alphas); + _debugger.add_request(Box::new(mem_req_2), alphas); + } + + combined_value +} + +// RESPONSES +// ================================================================================================ + +/// Builds the response from the memory chiplet at `row`. +pub(super) fn build_memory_chiplet_responses( + main_trace: &MainTrace, + row: RowIndex, + alphas: &[E], + _debugger: &mut BusDebugger, +) -> E +where + E: FieldElement, +{ + let access_type = main_trace.chiplet_selector_4(row); + let op_label = { + let is_read = main_trace.chiplet_selector_3(row); + get_memory_op_label(is_read, access_type) + }; + let ctx = main_trace.chiplet_memory_ctx(row); + let clk = main_trace.chiplet_memory_clk(row); + let addr = { + let word = main_trace.chiplet_memory_word(row); + let idx0 = main_trace.chiplet_memory_idx0(row); + let idx1 = main_trace.chiplet_memory_idx1(row); + + word + idx1.mul_small(2) + idx0 + }; + + let message: Box> = if access_type == MEMORY_ACCESS_ELEMENT { + let idx0 = main_trace.chiplet_memory_idx0(row); + let idx1 = main_trace.chiplet_memory_idx1(row); + + let element = if idx1 == ZERO && idx0 == ZERO { + main_trace.chiplet_memory_value_0(row) + } else if idx1 == ZERO && idx0 == ONE { + main_trace.chiplet_memory_value_1(row) + } else if idx1 == ONE && idx0 == ZERO { + main_trace.chiplet_memory_value_2(row) + } else if idx1 == ONE && idx0 == ONE { + main_trace.chiplet_memory_value_3(row) + } else { + panic!("Invalid word indices. idx0: {idx0}, idx1: {idx1}"); + }; + + let message = MemoryElementMessage { op_label, ctx, addr, clk, element }; + + Box::new(message) + } else if access_type == MEMORY_ACCESS_WORD { + let value0 = main_trace.chiplet_memory_value_0(row); + let value1 = main_trace.chiplet_memory_value_1(row); + let value2 = main_trace.chiplet_memory_value_2(row); + let value3 = main_trace.chiplet_memory_value_3(row); + + let message = MemoryWordMessage { + op_label, + ctx, + addr, + clk, + word: [value0, value1, value2, value3], + source: "memory chiplet", + }; + + Box::new(message) + } else { + panic!("Invalid memory element/word column value: {access_type}"); + }; + + let value = message.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + _debugger.add_response(message, alphas); + + value +} + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Returns the operation unique label for memory operations. +/// +/// The memory operation label is currently the only label that is built differently (or *simpler*) +/// from the other chiplets. We should refactor the other chiplets to use a similar (simpler) +/// approach. +fn get_memory_op_label(is_read: Felt, is_word_access: Felt) -> Felt { + const MEMORY_SELECTOR: u8 = 0b110; + // Equivalent to `is_read << 1` + let is_read_left_shift_1 = is_read + is_read; + + Felt::from(MEMORY_SELECTOR << 2) + is_read_left_shift_1 + is_word_access +} + +// MESSAGES +// =============================================================================================== + +pub struct MemoryWordMessage { + pub op_label: Felt, + pub ctx: Felt, + pub addr: Felt, + pub clk: Felt, + pub word: [Felt; 4], + pub source: &'static str, +} + +impl BusMessage for MemoryWordMessage +where + E: FieldElement, +{ + fn value(&self, alphas: &[E]) -> E { + alphas[0] + + build_value( + &alphas[1..9], + [ + self.op_label, + self.ctx, + self.addr, + self.clk, + self.word[0], + self.word[1], + self.word[2], + self.word[3], + ], + ) + } + + fn source(&self) -> &str { + self.source + } +} + +impl Display for MemoryWordMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!( + f, + "{{ op_label: {}, ctx: {}, addr: {}, clk: {}, word: {:?} }}", + self.op_label, self.ctx, self.addr, self.clk, self.word + ) + } +} + +pub struct MemoryElementMessage { + pub op_label: Felt, + pub ctx: Felt, + pub addr: Felt, + pub clk: Felt, + pub element: Felt, +} + +impl BusMessage for MemoryElementMessage +where + E: FieldElement, +{ + fn value(&self, alphas: &[E]) -> E { + alphas[0] + + build_value( + &alphas[1..6], + [self.op_label, self.ctx, self.addr, self.clk, self.element], + ) + } + + fn source(&self) -> &str { + "memory element" + } +} + +impl Display for MemoryElementMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { + write!( + f, + "{{ op_label: {}, ctx: {}, addr: {}, clk: {}, element: {} }}", + self.op_label, self.ctx, self.addr, self.clk, self.element + ) + } +} diff --git a/processor/src/chiplets/aux_trace/bus/mod.rs b/processor/src/chiplets/aux_trace/bus/mod.rs new file mode 100644 index 0000000000..708af306bc --- /dev/null +++ b/processor/src/chiplets/aux_trace/bus/mod.rs @@ -0,0 +1,241 @@ +use bitwise::{build_bitwise_chiplet_responses, build_bitwise_request}; +use hasher::{ + build_control_block_request, build_end_block_request, build_hasher_chiplet_responses, + build_hperm_request, build_mpverify_request, build_mrupdate_request, + build_respan_block_request, build_span_block_request, ControlBlockRequestMessage, +}; +use kernel::{build_kernel_chiplet_responses, KernelRomMessage}; +use memory::{ + build_mem_mload_mstore_request, build_mem_mloadw_mstorew_request, + build_memory_chiplet_responses, build_mstream_request, build_pipe_request, + build_rcomb_base_request, MemoryWordMessage, +}; +use miden_air::{ + trace::{ + chiplets::{ + hasher::LINEAR_HASH_LABEL, + memory::{ + MEMORY_READ_ELEMENT_LABEL, MEMORY_READ_WORD_LABEL, MEMORY_WRITE_ELEMENT_LABEL, + MEMORY_WRITE_WORD_LABEL, + }, + }, + main_trace::MainTrace, + }, + RowIndex, +}; +use vm_core::{ + ONE, OPCODE_CALL, OPCODE_DYN, OPCODE_DYNCALL, OPCODE_END, OPCODE_HPERM, OPCODE_JOIN, + OPCODE_LOOP, OPCODE_MLOAD, OPCODE_MLOADW, OPCODE_MPVERIFY, OPCODE_MRUPDATE, OPCODE_MSTORE, + OPCODE_MSTOREW, OPCODE_MSTREAM, OPCODE_PIPE, OPCODE_RCOMBBASE, OPCODE_RESPAN, OPCODE_SPAN, + OPCODE_SPLIT, OPCODE_SYSCALL, OPCODE_U32AND, OPCODE_U32XOR, ZERO, +}; + +use super::{Felt, FieldElement}; +use crate::{ + debug::{BusDebugger, BusMessage}, + trace::AuxColumnBuilder, +}; + +mod bitwise; +mod hasher; +mod kernel; +mod memory; + +// BUS COLUMN BUILDER +// ================================================================================================ + +/// Describes how to construct the execution trace of the chiplets bus auxiliary trace column. +#[derive(Default)] +pub struct BusColumnBuilder {} + +impl> AuxColumnBuilder for BusColumnBuilder { + /// Constructs the requests made by the VM-components to the chiplets at `row`. + fn get_requests_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + debugger: &mut BusDebugger, + ) -> E + where + E: FieldElement, + { + let op_code_felt = main_trace.get_op_code(row); + let op_code = op_code_felt.as_int() as u8; + + match op_code { + OPCODE_JOIN | OPCODE_SPLIT | OPCODE_LOOP | OPCODE_CALL => build_control_block_request( + main_trace, + main_trace.decoder_hasher_state(row), + op_code_felt, + alphas, + row, + debugger, + ), + OPCODE_DYN | OPCODE_DYNCALL => { + build_dyn_block_request(main_trace, op_code_felt, alphas, row, debugger) + }, + OPCODE_SYSCALL => { + build_syscall_block_request(main_trace, op_code_felt, alphas, row, debugger) + }, + OPCODE_SPAN => build_span_block_request(main_trace, alphas, row, debugger), + OPCODE_RESPAN => build_respan_block_request(main_trace, alphas, row, debugger), + OPCODE_END => build_end_block_request(main_trace, alphas, row, debugger), + OPCODE_U32AND => build_bitwise_request(main_trace, ZERO, alphas, row, debugger), + OPCODE_U32XOR => build_bitwise_request(main_trace, ONE, alphas, row, debugger), + OPCODE_MLOADW => build_mem_mloadw_mstorew_request( + main_trace, + MEMORY_READ_WORD_LABEL, + alphas, + row, + debugger, + ), + OPCODE_MSTOREW => build_mem_mloadw_mstorew_request( + main_trace, + MEMORY_WRITE_WORD_LABEL, + alphas, + row, + debugger, + ), + OPCODE_MLOAD => build_mem_mload_mstore_request( + main_trace, + MEMORY_READ_ELEMENT_LABEL, + alphas, + row, + debugger, + ), + OPCODE_MSTORE => build_mem_mload_mstore_request( + main_trace, + MEMORY_WRITE_ELEMENT_LABEL, + alphas, + row, + debugger, + ), + OPCODE_MSTREAM => build_mstream_request(main_trace, alphas, row, debugger), + OPCODE_RCOMBBASE => build_rcomb_base_request(main_trace, alphas, row, debugger), + OPCODE_HPERM => build_hperm_request(main_trace, alphas, row, debugger), + OPCODE_MPVERIFY => build_mpverify_request(main_trace, alphas, row, debugger), + OPCODE_MRUPDATE => build_mrupdate_request(main_trace, alphas, row, debugger), + OPCODE_PIPE => build_pipe_request(main_trace, alphas, row, debugger), + _ => E::ONE, + } + } + + /// Constructs the responses from the chiplets to the other VM-components at `row`. + fn get_responses_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + debugger: &mut BusDebugger, + ) -> E + where + E: FieldElement, + { + if main_trace.is_hash_row(row) { + build_hasher_chiplet_responses(main_trace, row, alphas, debugger) + } else if main_trace.is_bitwise_row(row) { + build_bitwise_chiplet_responses(main_trace, row, alphas, debugger) + } else if main_trace.is_memory_row(row) { + build_memory_chiplet_responses(main_trace, row, alphas, debugger) + } else if main_trace.is_kernel_row(row) { + build_kernel_chiplet_responses(main_trace, row, alphas, debugger) + } else { + E::ONE + } + } +} + +// CHIPLETS REQUESTS TO MORE THAN ONE CHIPLET +// ================================================================================================ + +/// Builds requests made on a `DYN` or `DYNCALL` operation. +fn build_dyn_block_request>( + main_trace: &MainTrace, + op_code_felt: Felt, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let control_block_req = ControlBlockRequestMessage { + transition_label: Felt::from(LINEAR_HASH_LABEL + 16), + addr_next: main_trace.addr(row + 1), + op_code: op_code_felt, + decoder_hasher_state: [ZERO; 8], + }; + + let memory_req = MemoryWordMessage { + op_label: Felt::from(MEMORY_READ_WORD_LABEL), + ctx: main_trace.ctx(row), + addr: main_trace.stack_element(0, row), + clk: main_trace.clk(row), + word: main_trace.decoder_hasher_state_first_half(row), + source: if op_code_felt == OPCODE_DYNCALL.into() { + "dyncall" + } else { + "dyn" + }, + }; + + let combined_value = control_block_req.value(alphas) * memory_req.value(alphas); + #[cfg(any(test, feature = "bus-debugger"))] + { + use alloc::boxed::Box; + _debugger.add_request(Box::new(control_block_req), alphas); + _debugger.add_request(Box::new(memory_req), alphas); + } + + combined_value +} + +/// Builds requests made to kernel ROM chiplet when initializing a syscall block. +fn build_syscall_block_request>( + main_trace: &MainTrace, + op_code_felt: Felt, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, +) -> E { + let control_block_req = ControlBlockRequestMessage { + transition_label: Felt::from(LINEAR_HASH_LABEL + 16), + addr_next: main_trace.addr(row + 1), + op_code: op_code_felt, + decoder_hasher_state: main_trace.decoder_hasher_state(row), + }; + + let kernel_rom_req = KernelRomMessage { + kernel_proc_digest: main_trace.decoder_hasher_state(row)[0..4].try_into().unwrap(), + }; + + let combined_value = control_block_req.value(alphas) * kernel_rom_req.value(alphas); + + #[cfg(any(test, feature = "bus-debugger"))] + { + _debugger.add_request(alloc::boxed::Box::new(control_block_req), alphas); + _debugger.add_request(alloc::boxed::Box::new(kernel_rom_req), alphas); + } + + combined_value +} + +// HELPER FUNCTIONS +// ================================================================================================ + +/// Runs an inner product between the alphas and the elements. +#[inline(always)] +fn build_value, const N: usize>( + alphas: &[E], + elements: [Felt; N], +) -> E { + debug_assert_eq!(alphas.len(), elements.len()); + let mut value = E::ZERO; + for i in 0..N { + value += alphas[i].mul_base(elements[i]); + } + value +} + +/// Returns the operation unique label. +fn get_op_label(s0: Felt, s1: Felt, s2: Felt, s3: Felt) -> Felt { + s3.mul_small(1 << 3) + s2.mul_small(1 << 2) + s1.mul_small(2) + s0 + ONE +} diff --git a/processor/src/chiplets/aux_trace/mod.rs b/processor/src/chiplets/aux_trace/mod.rs index d00987fd94..9a6b9511e2 100644 --- a/processor/src/chiplets/aux_trace/mod.rs +++ b/processor/src/chiplets/aux_trace/mod.rs @@ -1,41 +1,15 @@ use alloc::vec::Vec; -use miden_air::{ - trace::{ - chiplets::{ - bitwise::OP_CYCLE_LEN as BITWISE_OP_CYCLE_LEN, - hasher::{ - CAPACITY_LEN, DIGEST_RANGE, HASH_CYCLE_LEN, LINEAR_HASH_LABEL, MP_VERIFY_LABEL, - MR_UPDATE_NEW_LABEL, MR_UPDATE_OLD_LABEL, NUM_ROUNDS, RETURN_HASH_LABEL, - RETURN_STATE_LABEL, STATE_WIDTH, - }, - kernel_rom::KERNEL_PROC_LABEL, - memory::{ - MEMORY_ACCESS_ELEMENT, MEMORY_ACCESS_WORD, MEMORY_READ_ELEMENT_LABEL, - MEMORY_READ_WORD_LABEL, MEMORY_WRITE_ELEMENT_LABEL, MEMORY_WRITE_WORD_LABEL, - }, - }, - main_trace::MainTrace, - }, - RowIndex, -}; -use vm_core::{ - Kernel, Word, ONE, OPCODE_CALL, OPCODE_DYN, OPCODE_DYNCALL, OPCODE_END, OPCODE_HPERM, - OPCODE_JOIN, OPCODE_LOOP, OPCODE_MLOAD, OPCODE_MLOADW, OPCODE_MPVERIFY, OPCODE_MRUPDATE, - OPCODE_MSTORE, OPCODE_MSTOREW, OPCODE_MSTREAM, OPCODE_PIPE, OPCODE_RCOMBBASE, OPCODE_RESPAN, - OPCODE_SPAN, OPCODE_SPLIT, OPCODE_SYSCALL, OPCODE_U32AND, OPCODE_U32XOR, ZERO, -}; +use miden_air::trace::main_trace::MainTrace; +use vm_core::Kernel; use super::{super::trace::AuxColumnBuilder, Felt, FieldElement}; -// CONSTANTS -// ================================================================================================ +mod bus; +pub use bus::BusColumnBuilder; -const NUM_HEADER_ALPHAS: usize = 4; -const FOUR: Felt = Felt::new(4); - -// CHIPLETS AUXILIARY TRACE BUILDER -// ================================================================================================ +mod virtual_table; +pub use virtual_table::ChipletsVTableColBuilder; /// Constructs the execution trace for chiplets-related auxiliary columns (used in multiset checks). pub struct AuxTraceBuilder { @@ -67,1000 +41,7 @@ impl AuxTraceBuilder { let b_chip = bus_col_builder.build_aux_column(main_trace, rand_elements); debug_assert_eq!(*t_chip.last().unwrap(), E::ONE); - // TODO: Fix and re-enable after testing with miden-base - // debug_assert_eq!(*b_chip.last().unwrap(), E::ONE); + debug_assert_eq!(*b_chip.last().unwrap(), E::ONE); vec![t_chip, b_chip] } } - -// VIRTUAL TABLE COLUMN BUILDER -// ================================================================================================ - -/// Describes how to construct the execution trace of the chiplets virtual table auxiliary trace -/// column. -pub struct ChipletsVTableColBuilder { - kernel: Kernel, -} - -impl ChipletsVTableColBuilder { - fn new(kernel: Kernel) -> Self { - Self { kernel } - } -} - -impl> AuxColumnBuilder for ChipletsVTableColBuilder { - fn init_requests(&self, _main_trace: &MainTrace, alphas: &[E]) -> E { - let mut requests = E::ONE; - for (idx, proc_hash) in self.kernel.proc_hashes().iter().enumerate() { - requests *= alphas[0] - + alphas[1].mul_base((idx as u32).into()) - + alphas[2].mul_base(proc_hash[0]) - + alphas[3].mul_base(proc_hash[1]) - + alphas[4].mul_base(proc_hash[2]) - + alphas[5].mul_base(proc_hash[3]); - } - requests - } - - fn get_requests_at(&self, main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E { - chiplets_vtable_remove_sibling(main_trace, alphas, row) - } - - fn get_responses_at(&self, main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E { - chiplets_vtable_add_sibling(main_trace, alphas, row) - * build_kernel_procedure_table_inclusions(main_trace, alphas, row) - } -} - -// VIRTUAL TABLE REQUESTS -// ================================================================================================ - -/// Constructs the removals from the table when the hasher absorbs a new sibling node while -/// computing the new Merkle root. -fn chiplets_vtable_remove_sibling(main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E -where - E: FieldElement, -{ - let f_mu: bool = main_trace.f_mu(row); - let f_mua: bool = main_trace.f_mua(row); - - if f_mu { - let index = main_trace.chiplet_node_index(row); - let lsb = index.as_int() & 1; - if lsb == 0 { - let sibling = &main_trace.chiplet_hasher_state(row)[DIGEST_RANGE.end..]; - alphas[0] - + alphas[3].mul_base(index) - + alphas[12].mul_base(sibling[0]) - + alphas[13].mul_base(sibling[1]) - + alphas[14].mul_base(sibling[2]) - + alphas[15].mul_base(sibling[3]) - } else { - let sibling = &main_trace.chiplet_hasher_state(row)[DIGEST_RANGE]; - alphas[0] - + alphas[3].mul_base(index) - + alphas[8].mul_base(sibling[0]) - + alphas[9].mul_base(sibling[1]) - + alphas[10].mul_base(sibling[2]) - + alphas[11].mul_base(sibling[3]) - } - } else if f_mua { - let index = main_trace.chiplet_node_index(row); - let lsb = index.as_int() & 1; - if lsb == 0 { - let sibling = &main_trace.chiplet_hasher_state(row + 1)[DIGEST_RANGE.end..]; - alphas[0] - + alphas[3].mul_base(index) - + alphas[12].mul_base(sibling[0]) - + alphas[13].mul_base(sibling[1]) - + alphas[14].mul_base(sibling[2]) - + alphas[15].mul_base(sibling[3]) - } else { - let sibling = &main_trace.chiplet_hasher_state(row + 1)[DIGEST_RANGE]; - alphas[0] - + alphas[3].mul_base(index) - + alphas[8].mul_base(sibling[0]) - + alphas[9].mul_base(sibling[1]) - + alphas[10].mul_base(sibling[2]) - + alphas[11].mul_base(sibling[3]) - } - } else { - E::ONE - } -} - -// VIRTUAL TABLE RESPONSES -// ================================================================================================ - -/// Constructs the inclusions to the table when the hasher absorbs a new sibling node while -/// computing the old Merkle root. -fn chiplets_vtable_add_sibling(main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E -where - E: FieldElement, -{ - let f_mv: bool = main_trace.f_mv(row); - let f_mva: bool = main_trace.f_mva(row); - - if f_mv { - let index = main_trace.chiplet_node_index(row); - let lsb = index.as_int() & 1; - if lsb == 0 { - let sibling = &main_trace.chiplet_hasher_state(row)[DIGEST_RANGE.end..]; - alphas[0] - + alphas[3].mul_base(index) - + alphas[12].mul_base(sibling[0]) - + alphas[13].mul_base(sibling[1]) - + alphas[14].mul_base(sibling[2]) - + alphas[15].mul_base(sibling[3]) - } else { - let sibling = &main_trace.chiplet_hasher_state(row)[DIGEST_RANGE]; - alphas[0] - + alphas[3].mul_base(index) - + alphas[8].mul_base(sibling[0]) - + alphas[9].mul_base(sibling[1]) - + alphas[10].mul_base(sibling[2]) - + alphas[11].mul_base(sibling[3]) - } - } else if f_mva { - let index = main_trace.chiplet_node_index(row); - let lsb = index.as_int() & 1; - if lsb == 0 { - let sibling = &main_trace.chiplet_hasher_state(row + 1)[DIGEST_RANGE.end..]; - alphas[0] - + alphas[3].mul_base(index) - + alphas[12].mul_base(sibling[0]) - + alphas[13].mul_base(sibling[1]) - + alphas[14].mul_base(sibling[2]) - + alphas[15].mul_base(sibling[3]) - } else { - let sibling = &main_trace.chiplet_hasher_state(row + 1)[DIGEST_RANGE]; - alphas[0] - + alphas[3].mul_base(index) - + alphas[8].mul_base(sibling[0]) - + alphas[9].mul_base(sibling[1]) - + alphas[10].mul_base(sibling[2]) - + alphas[11].mul_base(sibling[3]) - } - } else { - E::ONE - } -} - -/// Builds the inclusions to the kernel procedure table at `row`. -fn build_kernel_procedure_table_inclusions( - main_trace: &MainTrace, - alphas: &[E], - row: RowIndex, -) -> E -where - E: FieldElement, -{ - if main_trace.is_kernel_row(row) { - let idx = main_trace.chiplet_kernel_idx(row); - let idx_delta = { - let idx_next = main_trace.chiplet_kernel_idx(row + 1); - idx_next - idx - }; - let next_row_is_kernel = main_trace.is_kernel_row(row + 1); - - // We want to add an entry to the table in 2 cases: - // 1. when the next row is a kernel row and the idx changes - // - this adds the last row of all rows that share the same idx - // 2. when the next row is not a kernel row - // - this is the edge case of (1) - if !next_row_is_kernel || idx_delta == ONE { - let root0 = main_trace.chiplet_kernel_root_0(row); - let root1 = main_trace.chiplet_kernel_root_1(row); - let root2 = main_trace.chiplet_kernel_root_2(row); - let root3 = main_trace.chiplet_kernel_root_3(row); - - alphas[0] - + alphas[1].mul_base(idx) - + alphas[2].mul_base(root0) - + alphas[3].mul_base(root1) - + alphas[4].mul_base(root2) - + alphas[5].mul_base(root3) - } else { - E::ONE - } - } else { - E::ONE - } -} - -// BUS COLUMN BUILDER -// ================================================================================================ - -/// Describes how to construct the execution trace of the chiplets bus auxiliary trace column. -#[derive(Default)] -pub struct BusColumnBuilder {} - -impl> AuxColumnBuilder for BusColumnBuilder { - /// Constructs the requests made by the VM-components to the chiplets at `row`. - fn get_requests_at(&self, main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E - where - E: FieldElement, - { - let op_code_felt = main_trace.get_op_code(row); - let op_code = op_code_felt.as_int() as u8; - - match op_code { - OPCODE_JOIN | OPCODE_SPLIT | OPCODE_LOOP | OPCODE_CALL => build_control_block_request( - main_trace, - main_trace.decoder_hasher_state(row), - op_code_felt, - alphas, - row, - ), - OPCODE_DYN | OPCODE_DYNCALL => { - build_dyn_block_request(main_trace, op_code_felt, alphas, row) - }, - OPCODE_SYSCALL => build_syscall_block_request(main_trace, op_code_felt, alphas, row), - OPCODE_SPAN => build_span_block_request(main_trace, alphas, row), - OPCODE_RESPAN => build_respan_block_request(main_trace, alphas, row), - OPCODE_END => build_end_block_request(main_trace, alphas, row), - OPCODE_U32AND => build_bitwise_request(main_trace, ZERO, alphas, row), - OPCODE_U32XOR => build_bitwise_request(main_trace, ONE, alphas, row), - OPCODE_MLOADW => { - build_mem_mloadw_mstorew_request(main_trace, MEMORY_READ_WORD_LABEL, alphas, row) - }, - OPCODE_MSTOREW => { - build_mem_mloadw_mstorew_request(main_trace, MEMORY_WRITE_WORD_LABEL, alphas, row) - }, - OPCODE_MLOAD => { - build_mem_mload_mstore_request(main_trace, MEMORY_READ_ELEMENT_LABEL, alphas, row) - }, - OPCODE_MSTORE => { - build_mem_mload_mstore_request(main_trace, MEMORY_WRITE_ELEMENT_LABEL, alphas, row) - }, - OPCODE_MSTREAM => build_mstream_request(main_trace, alphas, row), - OPCODE_RCOMBBASE => build_rcomb_base_request(main_trace, alphas, row), - OPCODE_HPERM => build_hperm_request(main_trace, alphas, row), - OPCODE_MPVERIFY => build_mpverify_request(main_trace, alphas, row), - OPCODE_MRUPDATE => build_mrupdate_request(main_trace, alphas, row), - OPCODE_PIPE => build_pipe_request(main_trace, alphas, row), - _ => E::ONE, - } - } - - /// Constructs the responses from the chiplets to the other VM-components at `row`. - fn get_responses_at(&self, main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E - where - E: FieldElement, - { - if main_trace.is_hash_row(row) { - build_hasher_chiplet_responses(main_trace, row, alphas) - } else if main_trace.is_bitwise_row(row) { - build_bitwise_chiplet_responses(main_trace, row, alphas) - } else if main_trace.is_memory_row(row) { - build_memory_chiplet_responses(main_trace, row, alphas) - } else if main_trace.is_kernel_row(row) { - build_kernel_chiplet_responses(main_trace, row, alphas) - } else { - E::ONE - } - } -} - -// CHIPLETS REQUESTS -// ================================================================================================ - -/// Builds requests made to the hasher chiplet at the start of a control block. -fn build_control_block_request>( - main_trace: &MainTrace, - decoder_hasher_state: [Felt; 8], - op_code_felt: Felt, - alphas: &[E], - row: RowIndex, -) -> E { - let op_label = LINEAR_HASH_LABEL; - let addr_nxt = main_trace.addr(row + 1); - let transition_label = op_label + 16; - - let header = - alphas[0] + alphas[1].mul_base(Felt::from(transition_label)) + alphas[2].mul_base(addr_nxt); - - header + build_value(&alphas[8..16], decoder_hasher_state) + alphas[5].mul_base(op_code_felt) -} - -/// Builds requests made on a `DYN` or `DYNCALL` operation. -fn build_dyn_block_request>( - main_trace: &MainTrace, - op_code_felt: Felt, - alphas: &[E], - row: RowIndex, -) -> E { - let control_block_req = - build_control_block_request(main_trace, [ZERO; 8], op_code_felt, alphas, row); - - let memory_req = { - let mem_addr = main_trace.stack_element(0, row); - let mem_value = main_trace.decoder_hasher_state_first_half(row); - - compute_mem_request_word( - main_trace, - MEMORY_READ_WORD_LABEL, - alphas, - row, - mem_addr, - mem_value, - ) - }; - - control_block_req * memory_req -} - -/// Builds requests made to kernel ROM chiplet when initializing a syscall block. -fn build_syscall_block_request>( - main_trace: &MainTrace, - op_code_felt: Felt, - alphas: &[E], - row: RowIndex, -) -> E { - let factor1 = build_control_block_request( - main_trace, - main_trace.decoder_hasher_state(row), - op_code_felt, - alphas, - row, - ); - - let op_label = KERNEL_PROC_LABEL; - let state = main_trace.decoder_hasher_state(row); - let factor2 = alphas[0] - + alphas[1].mul_base(op_label) - + alphas[2].mul_base(state[0]) - + alphas[3].mul_base(state[1]) - + alphas[4].mul_base(state[2]) - + alphas[5].mul_base(state[3]); - - factor1 * factor2 -} - -/// Builds requests made to the hasher chiplet at the start of a span block. -fn build_span_block_request>( - main_trace: &MainTrace, - alphas: &[E], - row: RowIndex, -) -> E { - let op_label = LINEAR_HASH_LABEL; - let addr_nxt = main_trace.addr(row + 1); - let transition_label = op_label + 16; - - let header = - alphas[0] + alphas[1].mul_base(Felt::from(transition_label)) + alphas[2].mul_base(addr_nxt); - - let state = main_trace.decoder_hasher_state(row); - header + build_value(&alphas[8..16], state) -} - -/// Builds requests made to the hasher chiplet at the start of a respan block. -fn build_respan_block_request>( - main_trace: &MainTrace, - alphas: &[E], - row: RowIndex, -) -> E { - let op_label = LINEAR_HASH_LABEL; - let addr_nxt = main_trace.addr(row + 1); - let transition_label = op_label + 32; - - let header = alphas[0] - + alphas[1].mul_base(Felt::from(transition_label)) - + alphas[2].mul_base(addr_nxt - ONE) - + alphas[3].mul_base(ZERO); - - let state = main_trace.decoder_hasher_state(row); - - header + build_value(&alphas[8..16], state) -} - -/// Builds requests made to the hasher chiplet at the end of a block. -fn build_end_block_request>( - main_trace: &MainTrace, - alphas: &[E], - row: RowIndex, -) -> E { - let op_label = RETURN_HASH_LABEL; - let addr = main_trace.addr(row) + Felt::from(NUM_ROUNDS as u8); - let transition_label = op_label + 32; - - let header = - alphas[0] + alphas[1].mul_base(Felt::from(transition_label)) + alphas[2].mul_base(addr); - - let state = main_trace.decoder_hasher_state(row); - let digest: [Felt; 4] = state[..4].try_into().unwrap(); - - header + build_value(&alphas[8..12], digest) -} - -/// Builds requests made to the bitwise chiplet. This can be either a request for the computation -/// of a `XOR` or an `AND` operation. -fn build_bitwise_request>( - main_trace: &MainTrace, - is_xor: Felt, - alphas: &[E], - row: RowIndex, -) -> E { - let op_label = get_op_label(ONE, ZERO, is_xor, ZERO); - let a = main_trace.stack_element(1, row); - let b = main_trace.stack_element(0, row); - let z = main_trace.stack_element(0, row + 1); - - alphas[0] + build_value(&alphas[1..5], [op_label, a, b, z]) -} - -/// Builds `MSTREAM` requests made to the memory chiplet. -fn build_mstream_request>( - main_trace: &MainTrace, - alphas: &[E], - row: RowIndex, -) -> E { - let word1 = [ - main_trace.stack_element(7, row + 1), - main_trace.stack_element(6, row + 1), - main_trace.stack_element(5, row + 1), - main_trace.stack_element(4, row + 1), - ]; - let word2 = [ - main_trace.stack_element(3, row + 1), - main_trace.stack_element(2, row + 1), - main_trace.stack_element(1, row + 1), - main_trace.stack_element(0, row + 1), - ]; - let addr = main_trace.stack_element(12, row); - let op_label = MEMORY_READ_WORD_LABEL; - - let factor1 = compute_mem_request_word(main_trace, op_label, alphas, row, addr, word1); - let factor2 = compute_mem_request_word(main_trace, op_label, alphas, row, addr + FOUR, word2); - - factor1 * factor2 -} - -/// Builds `PIPE` requests made to the memory chiplet. -fn build_pipe_request>( - main_trace: &MainTrace, - alphas: &[E], - row: RowIndex, -) -> E { - let word1 = [ - main_trace.stack_element(7, row + 1), - main_trace.stack_element(6, row + 1), - main_trace.stack_element(5, row + 1), - main_trace.stack_element(4, row + 1), - ]; - let word2 = [ - main_trace.stack_element(3, row + 1), - main_trace.stack_element(2, row + 1), - main_trace.stack_element(1, row + 1), - main_trace.stack_element(0, row + 1), - ]; - let addr = main_trace.stack_element(12, row); - let op_label = MEMORY_WRITE_WORD_LABEL; - - let req1 = compute_mem_request_word(main_trace, op_label, alphas, row, addr, word1); - let req2 = compute_mem_request_word(main_trace, op_label, alphas, row, addr + FOUR, word2); - - req1 * req2 -} - -/// Builds `RCOMBBASE` requests made to the memory chiplet. -fn build_rcomb_base_request>( - main_trace: &MainTrace, - alphas: &[E], - row: RowIndex, -) -> E { - let tz0 = main_trace.helper_register(0, row); - let tz1 = main_trace.helper_register(1, row); - let tzg0 = main_trace.helper_register(2, row); - let tzg1 = main_trace.helper_register(3, row); - let a0 = main_trace.helper_register(4, row); - let a1 = main_trace.helper_register(5, row); - let z_ptr = main_trace.stack_element(13, row); - let a_ptr = main_trace.stack_element(14, row); - let op_label = MEMORY_READ_WORD_LABEL; - - let req1 = - compute_mem_request_word(main_trace, op_label, alphas, row, z_ptr, [tz0, tz1, tzg0, tzg1]); - let req2 = - compute_mem_request_word(main_trace, op_label, alphas, row, a_ptr, [a0, a1, ZERO, ZERO]); - - req1 * req2 -} - -/// Builds `HPERM` requests made to the hash chiplet. -fn build_hperm_request>( - main_trace: &MainTrace, - alphas: &[E], - row: RowIndex, -) -> E { - let helper_0 = main_trace.helper_register(0, row); - - let s0_s12_cur = [ - main_trace.stack_element(0, row), - main_trace.stack_element(1, row), - main_trace.stack_element(2, row), - main_trace.stack_element(3, row), - main_trace.stack_element(4, row), - main_trace.stack_element(5, row), - main_trace.stack_element(6, row), - main_trace.stack_element(7, row), - main_trace.stack_element(8, row), - main_trace.stack_element(9, row), - main_trace.stack_element(10, row), - main_trace.stack_element(11, row), - ]; - - let s0_s12_nxt = [ - main_trace.stack_element(0, row + 1), - main_trace.stack_element(1, row + 1), - main_trace.stack_element(2, row + 1), - main_trace.stack_element(3, row + 1), - main_trace.stack_element(4, row + 1), - main_trace.stack_element(5, row + 1), - main_trace.stack_element(6, row + 1), - main_trace.stack_element(7, row + 1), - main_trace.stack_element(8, row + 1), - main_trace.stack_element(9, row + 1), - main_trace.stack_element(10, row + 1), - main_trace.stack_element(11, row + 1), - ]; - - let op_label = LINEAR_HASH_LABEL + 16; - - let sum_input = alphas[4..16] - .iter() - .rev() - .enumerate() - .fold(E::ZERO, |acc, (i, x)| acc + x.mul_base(s0_s12_cur[i])); - let v_input = alphas[0] - + alphas[1].mul_base(Felt::from(op_label)) - + alphas[2].mul_base(helper_0) - + sum_input; - - let op_label = RETURN_STATE_LABEL + 32; - - let sum_output = alphas[4..16] - .iter() - .rev() - .enumerate() - .fold(E::ZERO, |acc, (i, x)| acc + x.mul_base(s0_s12_nxt[i])); - let v_output = alphas[0] - + alphas[1].mul_base(Felt::from(op_label)) - + alphas[2].mul_base(helper_0 + Felt::new(7)) - + sum_output; - - v_input * v_output -} - -/// Builds `MPVERIFY` requests made to the hash chiplet. -fn build_mpverify_request>( - main_trace: &MainTrace, - alphas: &[E], - row: RowIndex, -) -> E { - let helper_0 = main_trace.helper_register(0, row); - - let s0_s3 = [ - main_trace.stack_element(0, row), - main_trace.stack_element(1, row), - main_trace.stack_element(2, row), - main_trace.stack_element(3, row), - ]; - let s4 = main_trace.stack_element(4, row); - let s5 = main_trace.stack_element(5, row); - let s6_s9 = [ - main_trace.stack_element(6, row), - main_trace.stack_element(7, row), - main_trace.stack_element(8, row), - main_trace.stack_element(9, row), - ]; - - let op_label = MP_VERIFY_LABEL + 16; - - let sum_input = alphas[8..12] - .iter() - .rev() - .enumerate() - .fold(E::ZERO, |acc, (i, x)| acc + x.mul_base(s0_s3[i])); - - let v_input = alphas[0] - + alphas[1].mul_base(Felt::from(op_label)) - + alphas[2].mul_base(helper_0) - + alphas[3].mul_base(s5) - + sum_input; - - let op_label = RETURN_HASH_LABEL + 32; - - let sum_output = alphas[8..12] - .iter() - .rev() - .enumerate() - .fold(E::ZERO, |acc, (i, x)| acc + x.mul_base(s6_s9[i])); - let v_output = alphas[0] - + alphas[1].mul_base(Felt::from(op_label)) - + alphas[2].mul_base(helper_0 + s4.mul_small(8) - ONE) - + sum_output; - - v_input * v_output -} - -/// Builds `MRUPDATE` requests made to the hash chiplet. -fn build_mrupdate_request>( - main_trace: &MainTrace, - alphas: &[E], - row: RowIndex, -) -> E { - let helper_0 = main_trace.helper_register(0, row); - - let s0_s3 = [ - main_trace.stack_element(0, row), - main_trace.stack_element(1, row), - main_trace.stack_element(2, row), - main_trace.stack_element(3, row), - ]; - let s0_s3_nxt = [ - main_trace.stack_element(0, row + 1), - main_trace.stack_element(1, row + 1), - main_trace.stack_element(2, row + 1), - main_trace.stack_element(3, row + 1), - ]; - let s4 = main_trace.stack_element(4, row); - let s5 = main_trace.stack_element(5, row); - let s6_s9 = [ - main_trace.stack_element(6, row), - main_trace.stack_element(7, row), - main_trace.stack_element(8, row), - main_trace.stack_element(9, row), - ]; - let s10_s13 = [ - main_trace.stack_element(10, row), - main_trace.stack_element(11, row), - main_trace.stack_element(12, row), - main_trace.stack_element(13, row), - ]; - - let op_label = MR_UPDATE_OLD_LABEL + 16; - - let sum_input = alphas[8..12] - .iter() - .rev() - .enumerate() - .fold(E::ZERO, |acc, (i, x)| acc + x.mul_base(s0_s3[i])); - let v_input_old = alphas[0] - + alphas[1].mul_base(Felt::from(op_label)) - + alphas[2].mul_base(helper_0) - + alphas[3].mul_base(s5) - + sum_input; - - let op_label = RETURN_HASH_LABEL + 32; - - let sum_output = alphas[8..12] - .iter() - .rev() - .enumerate() - .fold(E::ZERO, |acc, (i, x)| acc + x.mul_base(s6_s9[i])); - let v_output_old = alphas[0] - + alphas[1].mul_base(Felt::from(op_label)) - + alphas[2].mul_base(helper_0 + s4.mul_small(8) - ONE) - + sum_output; - - let op_label = MR_UPDATE_NEW_LABEL + 16; - let sum_input = alphas[8..12] - .iter() - .rev() - .enumerate() - .fold(E::ZERO, |acc, (i, x)| acc + x.mul_base(s10_s13[i])); - let v_input_new = alphas[0] - + alphas[1].mul_base(Felt::from(op_label)) - + alphas[2].mul_base(helper_0 + s4.mul_small(8)) - + alphas[3].mul_base(s5) - + sum_input; - - let op_label = RETURN_HASH_LABEL + 32; - - let sum_output = alphas[8..12] - .iter() - .rev() - .enumerate() - .fold(E::ZERO, |acc, (i, x)| acc + x.mul_base(s0_s3_nxt[i])); - let v_output_new = alphas[0] - + alphas[1].mul_base(Felt::from(op_label)) - + alphas[2].mul_base(helper_0 + s4.mul_small(16) - ONE) - + sum_output; - - v_input_new * v_input_old * v_output_new * v_output_old -} - -// CHIPLETS RESPONSES -// ================================================================================================ - -/// Builds the response from the hasher chiplet at `row`. -fn build_hasher_chiplet_responses(main_trace: &MainTrace, row: RowIndex, alphas: &[E]) -> E -where - E: FieldElement, -{ - let mut multiplicand = E::ONE; - let selector0 = main_trace.chiplet_selector_0(row); - let selector1 = main_trace.chiplet_selector_1(row); - let selector2 = main_trace.chiplet_selector_2(row); - let selector3 = main_trace.chiplet_selector_3(row); - let op_label = get_op_label(selector0, selector1, selector2, selector3); - - // f_bp, f_mp, f_mv or f_mu == 1 - if row.as_usize() % HASH_CYCLE_LEN == 0 { - let state = main_trace.chiplet_hasher_state(row); - let alphas_state = &alphas[NUM_HEADER_ALPHAS..(NUM_HEADER_ALPHAS + STATE_WIDTH)]; - let node_index = main_trace.chiplet_node_index(row); - let transition_label = op_label + Felt::from(16_u8); - - // f_bp == 1 - // v_all = v_h + v_a + v_b + v_c - if selector1 == ONE && selector2 == ZERO && selector3 == ZERO { - let header = alphas[0] - + build_value(&alphas[1..4], [transition_label, Felt::from(row + 1), node_index]); - - multiplicand = header + build_value(alphas_state, state); - } - - // f_mp or f_mv or f_mu == 1 - // v_leaf = v_h + (1 - b) * v_b + b * v_d - if selector1 == ONE && !(selector2 == ZERO && selector3 == ZERO) { - let header = alphas[0] - + build_value(&alphas[1..4], [transition_label, Felt::from(row + 1), node_index]); - - let bit = (node_index.as_int() & 1) as u8; - let left_word = build_value::<_, 4>( - &alphas_state[DIGEST_RANGE], - state[DIGEST_RANGE].try_into().unwrap(), - ); - let right_word = build_value::<_, 4>( - &alphas_state[DIGEST_RANGE], - state[DIGEST_RANGE.end..].try_into().unwrap(), - ); - - multiplicand = header + E::from(1 - bit).mul(left_word) + E::from(bit).mul(right_word); - } - } - - // f_hout, f_sout, f_abp == 1 - if row.as_usize() % HASH_CYCLE_LEN == HASH_CYCLE_LEN - 1 { - let state = main_trace.chiplet_hasher_state(row); - let alphas_state = &alphas[NUM_HEADER_ALPHAS..(NUM_HEADER_ALPHAS + STATE_WIDTH)]; - let node_index = main_trace.chiplet_node_index(row); - let transition_label = op_label + Felt::from(32_u8); - - // f_hout == 1 - // v_res = v_h + v_b; - if selector1 == ZERO && selector2 == ZERO && selector3 == ZERO { - let header = alphas[0] - + build_value(&alphas[1..4], [transition_label, Felt::from(row + 1), node_index]); - - multiplicand = header - + build_value::<_, 4>( - &alphas_state[DIGEST_RANGE], - state[DIGEST_RANGE].try_into().unwrap(), - ); - } - - // f_sout == 1 - // v_all = v_h + v_a + v_b + v_c - if selector1 == ZERO && selector2 == ZERO && selector3 == ONE { - let header = alphas[0] - + build_value(&alphas[1..4], [transition_label, Felt::from(row + 1), node_index]); - - multiplicand = header + build_value(alphas_state, state); - } - - // f_abp == 1 - // v_abp = v_h + v_b' + v_c' - v_b - v_c - if selector1 == ONE && selector2 == ZERO && selector3 == ZERO { - let header = alphas[0] - + build_value(&alphas[1..4], [transition_label, Felt::from(row + 1), node_index]); - - let state_nxt = main_trace.chiplet_hasher_state(row + 1); - - // build the value from the hasher state's just right after the absorption of new - // elements. - const SIZE: usize = STATE_WIDTH - CAPACITY_LEN; - let next_state_value = build_value::<_, SIZE>( - &alphas_state[CAPACITY_LEN..], - state_nxt[CAPACITY_LEN..].try_into().unwrap(), - ); - - multiplicand = header + next_state_value; - } - } - multiplicand -} - -/// Builds the response from the bitwise chiplet at `row`. -fn build_bitwise_chiplet_responses(main_trace: &MainTrace, row: RowIndex, alphas: &[E]) -> E -where - E: FieldElement, -{ - let is_xor = main_trace.chiplet_selector_2(row); - if row.as_usize() % BITWISE_OP_CYCLE_LEN == BITWISE_OP_CYCLE_LEN - 1 { - let op_label = get_op_label(ONE, ZERO, is_xor, ZERO); - - let a = main_trace.chiplet_bitwise_a(row); - let b = main_trace.chiplet_bitwise_b(row); - let z = main_trace.chiplet_bitwise_z(row); - - alphas[0] + build_value(&alphas[1..5], [op_label, a, b, z]) - } else { - E::ONE - } -} - -/// Builds the response from the memory chiplet at `row`. -fn build_memory_chiplet_responses(main_trace: &MainTrace, row: RowIndex, alphas: &[E]) -> E -where - E: FieldElement, -{ - let is_word_access = main_trace.chiplet_selector_4(row); - let header = { - let is_read = main_trace.chiplet_selector_3(row); - let op_label = get_memory_op_label(is_read, is_word_access); - - let ctx = main_trace.chiplet_memory_ctx(row); - let clk = main_trace.chiplet_memory_clk(row); - let address = { - let word = main_trace.chiplet_memory_word(row); - let idx0 = main_trace.chiplet_memory_idx0(row); - let idx1 = main_trace.chiplet_memory_idx1(row); - - word + idx1.mul_small(2) + idx0 - }; - - alphas[0] + build_value(&alphas[1..5], [op_label, ctx, address, clk]) - }; - - if is_word_access == MEMORY_ACCESS_ELEMENT { - let idx0 = main_trace.chiplet_memory_idx0(row); - let idx1 = main_trace.chiplet_memory_idx1(row); - - let value = if idx1 == ZERO && idx0 == ZERO { - main_trace.chiplet_memory_value_0(row) - } else if idx1 == ZERO && idx0 == ONE { - main_trace.chiplet_memory_value_1(row) - } else if idx1 == ONE && idx0 == ZERO { - main_trace.chiplet_memory_value_2(row) - } else if idx1 == ONE && idx0 == ONE { - main_trace.chiplet_memory_value_3(row) - } else { - panic!("Invalid word indices. idx0: {idx0}, idx1: {idx1}"); - }; - - header + alphas[5].mul_base(value) - } else if is_word_access == MEMORY_ACCESS_WORD { - let value0 = main_trace.chiplet_memory_value_0(row); - let value1 = main_trace.chiplet_memory_value_1(row); - let value2 = main_trace.chiplet_memory_value_2(row); - let value3 = main_trace.chiplet_memory_value_3(row); - - header + build_value(&alphas[5..9], [value0, value1, value2, value3]) - } else { - panic!("Invalid memory element/word column value: {is_word_access}"); - } -} - -/// Builds the response from the kernel chiplet at `row`. -fn build_kernel_chiplet_responses(main_trace: &MainTrace, row: RowIndex, alphas: &[E]) -> E -where - E: FieldElement, -{ - let op_label = KERNEL_PROC_LABEL; - - let root0 = main_trace.chiplet_kernel_root_0(row); - let root1 = main_trace.chiplet_kernel_root_1(row); - let root2 = main_trace.chiplet_kernel_root_2(row); - let root3 = main_trace.chiplet_kernel_root_3(row); - - let v = alphas[0] + build_value(&alphas[1..6], [op_label, root0, root1, root2, root3]); - - let kernel_chiplet_selector = main_trace.chiplet_selector_4(row); - v.mul_base(kernel_chiplet_selector) + E::from(ONE - kernel_chiplet_selector) -} - -// HELPER FUNCTIONS -// ================================================================================================ - -/// Runs an inner product between the alphas and the elements. -#[inline(always)] -fn build_value, const N: usize>( - alphas: &[E], - elements: [Felt; N], -) -> E { - debug_assert_eq!(alphas.len(), elements.len()); - let mut value = E::ZERO; - for i in 0..N { - value += alphas[i].mul_base(elements[i]); - } - value -} - -/// Returns the operation unique label. -fn get_op_label(s0: Felt, s1: Felt, s2: Felt, s3: Felt) -> Felt { - s3.mul_small(1 << 3) + s2.mul_small(1 << 2) + s1.mul_small(2) + s0 + ONE -} - -/// Returns the operation unique label for memory operations. -/// -/// The memory operation label is currently the only label that is built differently (or *simpler*) -/// from the other chiplets. We should refactor the other chiplets to use a similar (simpler) -/// approach. -fn get_memory_op_label(is_read: Felt, is_word_access: Felt) -> Felt { - const MEMORY_SELECTOR: u8 = 0b110; - // Equivalent to `is_read << 1` - let is_read_left_shift_1 = is_read + is_read; - - Felt::from(MEMORY_SELECTOR << 2) + is_read_left_shift_1 + is_word_access -} - -/// Builds `MLOADW` and `MSTOREW` requests made to the memory chiplet. -fn build_mem_mloadw_mstorew_request>( - main_trace: &MainTrace, - op_label: u8, - alphas: &[E], - row: RowIndex, -) -> E { - let word = [ - main_trace.stack_element(3, row + 1), - main_trace.stack_element(2, row + 1), - main_trace.stack_element(1, row + 1), - main_trace.stack_element(0, row + 1), - ]; - let addr = main_trace.stack_element(0, row); - - compute_mem_request_word(main_trace, op_label, alphas, row, addr, word) -} - -/// Builds `MLOAD` and `MSTORE` requests made to the memory chiplet. -fn build_mem_mload_mstore_request>( - main_trace: &MainTrace, - op_label: u8, - alphas: &[E], - row: RowIndex, -) -> E { - let element = main_trace.stack_element(0, row + 1); - let addr = main_trace.stack_element(0, row); - - compute_mem_request_element(main_trace, op_label, alphas, row, addr, element) -} - -/// Computes a memory request for a read or write of a single element. -fn compute_mem_request_element>( - main_trace: &MainTrace, - op_label: u8, - alphas: &[E], - row: RowIndex, - addr: Felt, - element: Felt, -) -> E { - debug_assert!(op_label == MEMORY_READ_ELEMENT_LABEL || op_label == MEMORY_WRITE_ELEMENT_LABEL); - - let ctx = main_trace.ctx(row); - let clk = main_trace.clk(row); - - alphas[0] + build_value(&alphas[1..6], [Felt::from(op_label), ctx, addr, clk, element]) -} - -/// Computes a memory request for a read or write of a word. -fn compute_mem_request_word>( - main_trace: &MainTrace, - op_label: u8, - alphas: &[E], - row: RowIndex, - addr: Felt, - word: Word, -) -> E { - debug_assert!(op_label == MEMORY_READ_WORD_LABEL || op_label == MEMORY_WRITE_WORD_LABEL); - let ctx = main_trace.ctx(row); - let clk = main_trace.clk(row); - - alphas[0] - + build_value( - &alphas[1..9], - [Felt::from(op_label), ctx, addr, clk, word[0], word[1], word[2], word[3]], - ) -} diff --git a/processor/src/chiplets/aux_trace/virtual_table.rs b/processor/src/chiplets/aux_trace/virtual_table.rs new file mode 100644 index 0000000000..8b0720e695 --- /dev/null +++ b/processor/src/chiplets/aux_trace/virtual_table.rs @@ -0,0 +1,217 @@ +use miden_air::{ + trace::{chiplets::hasher::DIGEST_RANGE, main_trace::MainTrace}, + RowIndex, +}; +use vm_core::{Kernel, ONE}; + +use super::{Felt, FieldElement}; +use crate::{debug::BusDebugger, trace::AuxColumnBuilder}; + +/// Describes how to construct the execution trace of the chiplets virtual table auxiliary trace +/// column. +pub struct ChipletsVTableColBuilder { + kernel: Kernel, +} + +impl ChipletsVTableColBuilder { + pub(super) fn new(kernel: Kernel) -> Self { + Self { kernel } + } +} + +impl> AuxColumnBuilder for ChipletsVTableColBuilder { + fn init_requests( + &self, + _main_trace: &MainTrace, + alphas: &[E], + _debugger: &mut BusDebugger, + ) -> E { + let mut requests = E::ONE; + for (idx, proc_hash) in self.kernel.proc_hashes().iter().enumerate() { + requests *= alphas[0] + + alphas[1].mul_base((idx as u32).into()) + + alphas[2].mul_base(proc_hash[0]) + + alphas[3].mul_base(proc_hash[1]) + + alphas[4].mul_base(proc_hash[2]) + + alphas[5].mul_base(proc_hash[3]); + } + requests + } + + fn get_requests_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, + ) -> E { + chiplets_vtable_remove_sibling(main_trace, alphas, row) + } + + fn get_responses_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, + ) -> E { + chiplets_vtable_add_sibling(main_trace, alphas, row) + * build_kernel_procedure_table_inclusions(main_trace, alphas, row) + } +} + +// VIRTUAL TABLE REQUESTS +// ================================================================================================ + +/// Constructs the removals from the table when the hasher absorbs a new sibling node while +/// computing the new Merkle root. +fn chiplets_vtable_remove_sibling(main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E +where + E: FieldElement, +{ + let f_mu: bool = main_trace.f_mu(row); + let f_mua: bool = main_trace.f_mua(row); + + if f_mu { + let index = main_trace.chiplet_node_index(row); + let lsb = index.as_int() & 1; + if lsb == 0 { + let sibling = &main_trace.chiplet_hasher_state(row)[DIGEST_RANGE.end..]; + alphas[0] + + alphas[3].mul_base(index) + + alphas[12].mul_base(sibling[0]) + + alphas[13].mul_base(sibling[1]) + + alphas[14].mul_base(sibling[2]) + + alphas[15].mul_base(sibling[3]) + } else { + let sibling = &main_trace.chiplet_hasher_state(row)[DIGEST_RANGE]; + alphas[0] + + alphas[3].mul_base(index) + + alphas[8].mul_base(sibling[0]) + + alphas[9].mul_base(sibling[1]) + + alphas[10].mul_base(sibling[2]) + + alphas[11].mul_base(sibling[3]) + } + } else if f_mua { + let index = main_trace.chiplet_node_index(row); + let lsb = index.as_int() & 1; + if lsb == 0 { + let sibling = &main_trace.chiplet_hasher_state(row + 1)[DIGEST_RANGE.end..]; + alphas[0] + + alphas[3].mul_base(index) + + alphas[12].mul_base(sibling[0]) + + alphas[13].mul_base(sibling[1]) + + alphas[14].mul_base(sibling[2]) + + alphas[15].mul_base(sibling[3]) + } else { + let sibling = &main_trace.chiplet_hasher_state(row + 1)[DIGEST_RANGE]; + alphas[0] + + alphas[3].mul_base(index) + + alphas[8].mul_base(sibling[0]) + + alphas[9].mul_base(sibling[1]) + + alphas[10].mul_base(sibling[2]) + + alphas[11].mul_base(sibling[3]) + } + } else { + E::ONE + } +} + +// VIRTUAL TABLE RESPONSES +// ================================================================================================ + +/// Constructs the inclusions to the table when the hasher absorbs a new sibling node while +/// computing the old Merkle root. +fn chiplets_vtable_add_sibling(main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E +where + E: FieldElement, +{ + let f_mv: bool = main_trace.f_mv(row); + let f_mva: bool = main_trace.f_mva(row); + + if f_mv { + let index = main_trace.chiplet_node_index(row); + let lsb = index.as_int() & 1; + if lsb == 0 { + let sibling = &main_trace.chiplet_hasher_state(row)[DIGEST_RANGE.end..]; + alphas[0] + + alphas[3].mul_base(index) + + alphas[12].mul_base(sibling[0]) + + alphas[13].mul_base(sibling[1]) + + alphas[14].mul_base(sibling[2]) + + alphas[15].mul_base(sibling[3]) + } else { + let sibling = &main_trace.chiplet_hasher_state(row)[DIGEST_RANGE]; + alphas[0] + + alphas[3].mul_base(index) + + alphas[8].mul_base(sibling[0]) + + alphas[9].mul_base(sibling[1]) + + alphas[10].mul_base(sibling[2]) + + alphas[11].mul_base(sibling[3]) + } + } else if f_mva { + let index = main_trace.chiplet_node_index(row); + let lsb = index.as_int() & 1; + if lsb == 0 { + let sibling = &main_trace.chiplet_hasher_state(row + 1)[DIGEST_RANGE.end..]; + alphas[0] + + alphas[3].mul_base(index) + + alphas[12].mul_base(sibling[0]) + + alphas[13].mul_base(sibling[1]) + + alphas[14].mul_base(sibling[2]) + + alphas[15].mul_base(sibling[3]) + } else { + let sibling = &main_trace.chiplet_hasher_state(row + 1)[DIGEST_RANGE]; + alphas[0] + + alphas[3].mul_base(index) + + alphas[8].mul_base(sibling[0]) + + alphas[9].mul_base(sibling[1]) + + alphas[10].mul_base(sibling[2]) + + alphas[11].mul_base(sibling[3]) + } + } else { + E::ONE + } +} + +/// Builds the inclusions to the kernel procedure table at `row`. +fn build_kernel_procedure_table_inclusions( + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, +) -> E +where + E: FieldElement, +{ + if main_trace.is_kernel_row(row) { + let idx = main_trace.chiplet_kernel_idx(row); + let idx_delta = { + let idx_next = main_trace.chiplet_kernel_idx(row + 1); + idx_next - idx + }; + let next_row_is_kernel = main_trace.is_kernel_row(row + 1); + + // We want to add an entry to the table in 2 cases: + // 1. when the next row is a kernel row and the idx changes + // - this adds the last row of all rows that share the same idx + // 2. when the next row is not a kernel row + // - this is the edge case of (1) + if !next_row_is_kernel || idx_delta == ONE { + let root0 = main_trace.chiplet_kernel_root_0(row); + let root1 = main_trace.chiplet_kernel_root_1(row); + let root2 = main_trace.chiplet_kernel_root_2(row); + let root3 = main_trace.chiplet_kernel_root_3(row); + + alphas[0] + + alphas[1].mul_base(idx) + + alphas[2].mul_base(root0) + + alphas[3].mul_base(root1) + + alphas[4].mul_base(root2) + + alphas[5].mul_base(root3) + } else { + E::ONE + } + } else { + E::ONE + } +} diff --git a/processor/src/debug.rs b/processor/src/debug.rs index 4826811a82..293f3d33c0 100644 --- a/processor/src/debug.rs +++ b/processor/src/debug.rs @@ -1,11 +1,12 @@ use alloc::{ + boxed::Box, string::{String, ToString}, vec::Vec, }; use core::fmt; use miden_air::RowIndex; -use vm_core::{AssemblyOp, Operation, StackOutputs}; +use vm_core::{AssemblyOp, FieldElement, Operation, StackOutputs}; use crate::{ range::RangeChecker, system::ContextId, Chiplets, ChipletsLengths, Decoder, ExecutionError, @@ -313,3 +314,116 @@ impl AsRef for AsmOpInfo { &self.asmop } } + +// BUS DEBUGGING +// ================================================================= + +/// A message that can be sent on a bus. +pub(crate) trait BusMessage>: fmt::Display { + /// The concrete value that this message evaluates to. + fn value(&self, alphas: &[E]) -> E; + + /// The source of this message (e.g. "mload" or "memory chiplet"). + fn source(&self) -> &str; +} + +/// A debugger for a bus that can be used to track outstanding requests and responses. +/// +/// Note: we use `Vec` internally instead of a `BTreeMap`, since messages can have collisions (i.e. +/// 2 messages sent with the same key), which results in relatively complex insertion/deletion +/// logic. Since this is only used in debug/test code, the performance hit is acceptable. +pub(crate) struct BusDebugger> { + pub bus_name: String, + pub outstanding_requests: Vec<(E, Box>)>, + pub outstanding_responses: Vec<(E, Box>)>, +} + +impl BusDebugger +where + E: FieldElement, +{ + pub fn new(bus_name: String) -> Self { + Self { + bus_name, + outstanding_requests: Vec::new(), + outstanding_responses: Vec::new(), + } + } +} + +impl BusDebugger +where + E: FieldElement, +{ + /// Attempts to match the request with an existing response. If a match is found, the response + /// is removed from the list of outstanding responses. Otherwise, the request is added to the + /// list of outstanding requests. + #[allow(dead_code)] + pub fn add_request(&mut self, request_msg: Box>, alphas: &[E]) { + let msg_value = request_msg.value(alphas); + + if let Some(pos) = + self.outstanding_responses.iter().position(|(value, _)| *value == msg_value) + { + self.outstanding_responses.swap_remove(pos); + } else { + self.outstanding_requests.push((msg_value, request_msg)); + } + } + + /// Attempts to match the response with an existing request. If a match is found, the request is + /// removed from the list of outstanding requests. Otherwise, the response is added to the list + /// of outstanding responses. + #[allow(dead_code)] + pub fn add_response(&mut self, response_msg: Box>, alphas: &[E]) { + let msg_value = response_msg.value(alphas); + + if let Some(pos) = + self.outstanding_requests.iter().position(|(value, _)| *value == msg_value) + { + self.outstanding_requests.swap_remove(pos); + } else { + self.outstanding_responses.push((msg_value, response_msg)); + } + } + + /// Returns true if there are no outstanding requests or responses. + /// + /// This is meant to be called at the end of filling the bus. If there are any outstanding + /// requests or responses, it means that there is a mismatch between the requests and responses, + /// and the test should fail. The `Debug` implementation for `BusDebugger` will print out the + /// outstanding requests and responses. + #[allow(dead_code)] + pub fn is_empty(&self) -> bool { + self.outstanding_requests.is_empty() && self.outstanding_responses.is_empty() + } +} + +impl fmt::Display for BusDebugger +where + E: FieldElement, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.is_empty() { + writeln!(f, "Bus '{}' is empty.", self.bus_name)?; + } else { + writeln!(f, "Bus '{}' construction failed.", self.bus_name)?; + + if !self.outstanding_requests.is_empty() { + writeln!(f, "The following requests are still outstanding:")?; + for (_value, msg) in &self.outstanding_requests { + writeln!(f, "- {}: {}", msg.source(), msg)?; + } + } + + if !self.outstanding_responses.is_empty() { + writeln!(f, "\nThe following responses are still outstanding:")?; + for (_value, msg) in &self.outstanding_responses { + writeln!(f, "- {}: {}", msg.source(), msg)?; + } + } + } + + Ok(()) + } +} diff --git a/processor/src/decoder/aux_trace/block_hash_table.rs b/processor/src/decoder/aux_trace/block_hash_table.rs index 75d831b1fb..f09145d67d 100644 --- a/processor/src/decoder/aux_trace/block_hash_table.rs +++ b/processor/src/decoder/aux_trace/block_hash_table.rs @@ -5,6 +5,7 @@ use vm_core::{ }; use super::{AuxColumnBuilder, Felt, FieldElement, MainTrace, ONE}; +use crate::debug::BusDebugger; // BLOCK HASH TABLE COLUMN BUILDER // ================================================================================================ @@ -24,12 +25,23 @@ use super::{AuxColumnBuilder, Felt, FieldElement, MainTrace, ONE}; pub struct BlockHashTableColumnBuilder {} impl> AuxColumnBuilder for BlockHashTableColumnBuilder { - fn init_responses(&self, main_trace: &MainTrace, alphas: &[E]) -> E { + fn init_responses( + &self, + main_trace: &MainTrace, + alphas: &[E], + _debugger: &mut BusDebugger, + ) -> E { BlockHashTableRow::table_init(main_trace).collapse(alphas) } /// Removes a row from the block hash table. - fn get_requests_at(&self, main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E { + fn get_requests_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, + ) -> E { let op_code = main_trace.get_op_code(row).as_int() as u8; match op_code { @@ -39,7 +51,13 @@ impl> AuxColumnBuilder for BlockHashTableCo } /// Adds a row to the block hash table. - fn get_responses_at(&self, main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E { + fn get_responses_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + _debugger: &mut BusDebugger, + ) -> E { let op_code = main_trace.get_op_code(row).as_int() as u8; match op_code { diff --git a/processor/src/decoder/aux_trace/block_stack_table.rs b/processor/src/decoder/aux_trace/block_stack_table.rs index e59c668617..b34a9a342a 100644 --- a/processor/src/decoder/aux_trace/block_stack_table.rs +++ b/processor/src/decoder/aux_trace/block_stack_table.rs @@ -5,6 +5,7 @@ use vm_core::{ }; use super::{AuxColumnBuilder, Felt, FieldElement, MainTrace, ONE, ZERO}; +use crate::debug::BusDebugger; // BLOCK STACK TABLE COLUMN BUILDER // ================================================================================================ @@ -16,7 +17,13 @@ pub struct BlockStackColumnBuilder {} impl> AuxColumnBuilder for BlockStackColumnBuilder { /// Removes a row from the block stack table. - fn get_requests_at(&self, main_trace: &MainTrace, alphas: &[E], i: RowIndex) -> E { + fn get_requests_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + i: RowIndex, + _debugger: &mut BusDebugger, + ) -> E { let op_code_felt = main_trace.get_op_code(i); let op_code = op_code_felt.as_int() as u8; @@ -28,7 +35,13 @@ impl> AuxColumnBuilder for BlockStackColumn } /// Adds a row to the block stack table. - fn get_responses_at(&self, main_trace: &MainTrace, alphas: &[E], i: RowIndex) -> E { + fn get_responses_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + i: RowIndex, + _debugger: &mut BusDebugger, + ) -> E { let op_code_felt = main_trace.get_op_code(i); let op_code = op_code_felt.as_int() as u8; diff --git a/processor/src/decoder/aux_trace/op_group_table.rs b/processor/src/decoder/aux_trace/op_group_table.rs index 5800a6b2e2..7c68819dc0 100644 --- a/processor/src/decoder/aux_trace/op_group_table.rs +++ b/processor/src/decoder/aux_trace/op_group_table.rs @@ -5,6 +5,7 @@ use miden_air::{ use vm_core::{OPCODE_EMIT, OPCODE_PUSH, OPCODE_RESPAN, OPCODE_SPAN}; use super::{AuxColumnBuilder, Felt, FieldElement, MainTrace, ONE}; +use crate::debug::BusDebugger; // OP GROUP TABLE COLUMN // ================================================================================================ @@ -16,7 +17,13 @@ pub struct OpGroupTableColumnBuilder {} impl> AuxColumnBuilder for OpGroupTableColumnBuilder { /// Removes a row from the block hash table. - fn get_requests_at(&self, main_trace: &MainTrace, alphas: &[E], i: RowIndex) -> E { + fn get_requests_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + i: RowIndex, + _debugger: &mut BusDebugger, + ) -> E { let delete_group_flag = main_trace.delta_group_count(i) * main_trace.is_in_span(i); if delete_group_flag == ONE { @@ -27,7 +34,13 @@ impl> AuxColumnBuilder for OpGroupTableColu } /// Adds a row to the block hash table. - fn get_responses_at(&self, main_trace: &MainTrace, alphas: &[E], i: RowIndex) -> E { + fn get_responses_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + i: RowIndex, + _debugger: &mut BusDebugger, + ) -> E { let op_code_felt = main_trace.get_op_code(i); let op_code = op_code_felt.as_int() as u8; diff --git a/processor/src/host/dsa.rs b/processor/src/host/dsa.rs index 5c7fa1c813..73060f062c 100644 --- a/processor/src/host/dsa.rs +++ b/processor/src/host/dsa.rs @@ -1,10 +1,6 @@ use alloc::vec::Vec; -use vm_core::{ - crypto::dsa::rpo_falcon512::{Polynomial, SecretKey}, - utils::Deserializable, - Felt, Word, -}; +use vm_core::{Felt, Word}; use crate::ExecutionError; @@ -24,6 +20,11 @@ use crate::ExecutionError; /// - The signature generation failed. #[cfg(feature = "std")] pub fn falcon_sign(sk: &[Felt], msg: Word) -> Result, ExecutionError> { + use vm_core::{ + crypto::dsa::rpo_falcon512::{Polynomial, SecretKey}, + utils::Deserializable, + }; + // Create the corresponding secret key let mut sk_bytes = Vec::with_capacity(sk.len()); for element in sk { diff --git a/processor/src/stack/aux_trace.rs b/processor/src/stack/aux_trace.rs index 8c34985756..09ab0f6159 100644 --- a/processor/src/stack/aux_trace.rs +++ b/processor/src/stack/aux_trace.rs @@ -4,7 +4,7 @@ use miden_air::{trace::main_trace::MainTrace, RowIndex}; use vm_core::OPCODE_DYNCALL; use super::{Felt, FieldElement, OverflowTableRow}; -use crate::trace::AuxColumnBuilder; +use crate::{debug::BusDebugger, trace::AuxColumnBuilder}; // AUXILIARY TRACE BUILDER // ================================================================================================ @@ -30,7 +30,13 @@ impl AuxTraceBuilder { impl> AuxColumnBuilder for AuxTraceBuilder { /// Removes a row from the stack overflow table. - fn get_requests_at(&self, main_trace: &MainTrace, alphas: &[E], i: RowIndex) -> E { + fn get_requests_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + i: RowIndex, + _debugger: &mut BusDebugger, + ) -> E { let is_left_shift = main_trace.is_left_shift(i); let is_dyncall = main_trace.get_op_code(i) == OPCODE_DYNCALL.into(); let is_non_empty_overflow = main_trace.is_non_empty_overflow(i); @@ -53,7 +59,13 @@ impl> AuxColumnBuilder for AuxTraceBuilder } /// Adds a row to the stack overflow table. - fn get_responses_at(&self, main_trace: &MainTrace, alphas: &[E], i: RowIndex) -> E { + fn get_responses_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + i: RowIndex, + _debugger: &mut BusDebugger, + ) -> E { let is_right_shift = main_trace.is_right_shift(i); if is_right_shift { diff --git a/processor/src/trace/tests/hasher.rs b/processor/src/trace/tests/hasher.rs index 7197c925f5..f333167010 100644 --- a/processor/src/trace/tests/hasher.rs +++ b/processor/src/trace/tests/hasher.rs @@ -3,6 +3,7 @@ use alloc::vec::Vec; use miden_air::trace::{ chiplets::hasher::P1_COL_IDX, main_trace::MainTrace, AUX_TRACE_RAND_ELEMENTS, }; +use rstest::rstest; use vm_core::{ crypto::merkle::{MerkleStore, MerkleTree, NodeIndex}, FieldElement, @@ -17,17 +18,19 @@ use crate::StackInputs; // SIBLING TABLE TESTS // ================================================================================================ -#[test] -#[allow(clippy::needless_range_loop)] -fn hasher_p1_mp_verify() { +#[rstest] +#[case(5_u64)] +#[case(4_u64)] +fn hasher_p1_mp_verify(#[case] index: u64) { let (tree, _) = build_merkle_tree(); let store = MerkleStore::from(&tree); - let node = tree.get_node(NodeIndex::new(3, 1).unwrap()).unwrap(); + let depth = 3; + let node = tree.get_node(NodeIndex::new(depth as u8, index).unwrap()).unwrap(); // build program inputs let mut init_stack = vec![]; append_word(&mut init_stack, node.into()); - init_stack.extend_from_slice(&[3, 1]); + init_stack.extend_from_slice(&[depth, index]); append_word(&mut init_stack, tree.root().into()); init_stack.reverse(); let stack_inputs = StackInputs::try_from_ints(init_stack).unwrap(); @@ -42,16 +45,16 @@ fn hasher_p1_mp_verify() { // executing MPVERIFY does not affect the sibling table - so, all values in the column must be // ONE - for i in 0..(p1.len() - NUM_RAND_ROWS) { - assert_eq!(ONE, p1[i]); + for value in p1.iter().take(p1.len() - NUM_RAND_ROWS) { + assert_eq!(ONE, *value); } } -#[test] -#[allow(clippy::needless_range_loop)] -fn hasher_p1_mr_update() { +#[rstest] +#[case(5_u64)] +#[case(4_u64)] +fn hasher_p1_mr_update(#[case] index: u64) { let (tree, _) = build_merkle_tree(); - let index = 5_u64; let old_node = tree.get_node(NodeIndex::new(3, index).unwrap()).unwrap(); let new_node = init_leaf(11); let path = tree.get_path(NodeIndex::new(3, index).unwrap()).unwrap(); @@ -89,8 +92,8 @@ fn hasher_p1_mr_update() { // the running product does not change for the next 7 steps because the hasher computes the // hash of the SPAN block - for i in 1..8 { - assert_eq!(expected_value, p1[i]); + for value in p1.iter().take(8).skip(1) { + assert_eq!(expected_value, *value); } // on step 8, computations of the "old Merkle root" is started and the first sibling is added @@ -99,8 +102,8 @@ fn hasher_p1_mr_update() { assert_eq!(expected_value, p1[9]); // and then again for the next 6 steps the value remains the same - for i in 10..16 { - assert_eq!(expected_value, p1[i]); + for value in p1.iter().take(16).skip(10) { + assert_eq!(expected_value, *value); } // on step 15, the next sibling is added to the table in the following row (step 16) @@ -108,8 +111,8 @@ fn hasher_p1_mr_update() { assert_eq!(expected_value, p1[16]); // and then again for the next 6 steps the value remains the same - for i in 18..24 { - assert_eq!(expected_value, p1[i]); + for value in p1.iter().take(24).skip(18) { + assert_eq!(expected_value, *value); } // on step 23, the last sibling is added to the table in the following row (step 24) @@ -117,8 +120,8 @@ fn hasher_p1_mr_update() { assert_eq!(expected_value, p1[24]); // and then again for the next 7 steps the value remains the same - for i in 25..33 { - assert_eq!(expected_value, p1[i]); + for value in p1.iter().take(33).skip(25) { + assert_eq!(expected_value, *value); } // on step 32, computations of the "new Merkle root" is started and the first sibling is @@ -127,8 +130,8 @@ fn hasher_p1_mr_update() { assert_eq!(expected_value, p1[33]); // then, for the next 6 steps the value remains the same - for i in 33..40 { - assert_eq!(expected_value, p1[i]); + for value in p1.iter().take(40).skip(33) { + assert_eq!(expected_value, *value); } // on step 39, the next sibling is removed from the table in the following row (step 40) @@ -136,8 +139,8 @@ fn hasher_p1_mr_update() { assert_eq!(expected_value, p1[40]); // and then again for the next 6 steps the value remains the same - for i in 41..48 { - assert_eq!(expected_value, p1[i]); + for value in p1.iter().take(48).skip(41) { + assert_eq!(expected_value, *value); } // on step 47, the last sibling is removed from the table in the following row (step 48) @@ -146,8 +149,8 @@ fn hasher_p1_mr_update() { // at this point the table should be empty again, and it should stay empty until the end assert_eq!(expected_value, ONE); - for i in 50..(p1.len() - NUM_RAND_ROWS) { - assert_eq!(ONE, p1[i]); + for value in p1.iter().skip(50).take(p1.len() - NUM_RAND_ROWS - 50) { + assert_eq!(ONE, *value); } } diff --git a/processor/src/trace/utils.rs b/processor/src/trace/utils.rs index bee240455d..ac9f72f15a 100644 --- a/processor/src/trace/utils.rs +++ b/processor/src/trace/utils.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{string::ToString, vec::Vec}; use core::slice; use miden_air::{trace::main_trace::MainTrace, RowIndex}; @@ -6,7 +6,7 @@ use miden_air::{trace::main_trace::MainTrace, RowIndex}; use vm_core::{utils::ToElements, Operation}; use super::{Felt, FieldElement, NUM_RAND_ROWS}; -use crate::{chiplets::Chiplets, utils::uninit_vector}; +use crate::{chiplets::Chiplets, debug::BusDebugger, utils::uninit_vector}; // TRACE FRAGMENT // ================================================================================================ @@ -209,18 +209,40 @@ pub trait AuxColumnBuilder> { // REQUIRED METHODS // -------------------------------------------------------------------------------------------- - fn get_requests_at(&self, main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E; - - fn get_responses_at(&self, main_trace: &MainTrace, alphas: &[E], row: RowIndex) -> E; + fn get_requests_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + debugger: &mut BusDebugger, + ) -> E; + + fn get_responses_at( + &self, + main_trace: &MainTrace, + alphas: &[E], + row: RowIndex, + debugger: &mut BusDebugger, + ) -> E; // PROVIDED METHODS // -------------------------------------------------------------------------------------------- - fn init_requests(&self, _main_trace: &MainTrace, _alphas: &[E]) -> E { + fn init_requests( + &self, + _main_trace: &MainTrace, + _alphas: &[E], + _debugger: &mut BusDebugger, + ) -> E { E::ONE } - fn init_responses(&self, _main_trace: &MainTrace, _alphas: &[E]) -> E { + fn init_responses( + &self, + _main_trace: &MainTrace, + _alphas: &[E], + _debugger: &mut BusDebugger, + ) -> E { E::ONE } @@ -228,16 +250,18 @@ pub trait AuxColumnBuilder> { fn build_aux_column(&self, main_trace: &MainTrace, alphas: &[E]) -> Vec { let mut responses_prod: Vec = unsafe { uninit_vector(main_trace.num_rows()) }; let mut requests: Vec = unsafe { uninit_vector(main_trace.num_rows()) }; + let mut bus_debugger = BusDebugger::new("chiplets bus".to_string()); - responses_prod[0] = self.init_responses(main_trace, alphas); - requests[0] = self.init_requests(main_trace, alphas); + responses_prod[0] = self.init_responses(main_trace, alphas, &mut bus_debugger); + requests[0] = self.init_requests(main_trace, alphas, &mut bus_debugger); let mut requests_running_prod = requests[0]; for row_idx in 0..main_trace.num_rows() - 1 { let row = row_idx.into(); - responses_prod[row_idx + 1] = - responses_prod[row_idx] * self.get_responses_at(main_trace, alphas, row); - requests[row_idx + 1] = self.get_requests_at(main_trace, alphas, row); + responses_prod[row_idx + 1] = responses_prod[row_idx] + * self.get_responses_at(main_trace, alphas, row, &mut bus_debugger); + requests[row_idx + 1] = + self.get_requests_at(main_trace, alphas, row, &mut bus_debugger); requests_running_prod *= requests[row_idx + 1]; } @@ -247,6 +271,10 @@ pub trait AuxColumnBuilder> { result_aux_column[i] *= requests_running_divisor; requests_running_divisor *= requests[i]; } + + #[cfg(any(test, feature = "bus-debugger"))] + assert!(bus_debugger.is_empty(), "{bus_debugger}"); + result_aux_column } }