From c033c0e9ca4a4564d7431b86a482ce62348d959e Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Fri, 17 May 2024 14:19:28 +0100 Subject: [PATCH 01/15] add test --- aggregator/src/aggregation/decoder.rs | 66 ++++++++++++++++++++ aggregator/src/aggregation/decoder/witgen.rs | 40 +++++++----- 2 files changed, 90 insertions(+), 16 deletions(-) diff --git a/aggregator/src/aggregation/decoder.rs b/aggregator/src/aggregation/decoder.rs index ee930ae804..008c1040a0 100644 --- a/aggregator/src/aggregation/decoder.rs +++ b/aggregator/src/aggregation/decoder.rs @@ -5203,4 +5203,70 @@ mod tests { Ok(()) } + + #[test] + fn test_decoder_config_multi_block() -> Result<(), std::io::Error> { + let mut batch_files = fs::read_dir("./data/test_batches")? + .map(|entry| entry.map(|e| e.path())) + .collect::, std::io::Error>>()?; + batch_files.sort(); + + let batches = batch_files + .iter() + .map(fs::read_to_string) + .filter_map(|data| data.ok()) + .map(|data| hex::decode(data.trim_end()).expect("Failed to decode hex data")) + .collect::>>(); + + let raw = batches[1].clone(); + let compressed = { + // compression level = 0 defaults to using level=3, which is zstd's default. + let mut encoder = + zstd::stream::write::Encoder::new(Vec::new(), 0).expect("Encoder construction"); + + // disable compression of literals, i.e. literals will be raw bytes. + encoder + .set_parameter(zstd::stream::raw::CParameter::LiteralCompressionMode( + zstd::zstd_safe::ParamSwitch::Disable, + )) + .expect("Encoder set_parameter: LiteralCompressionMode"); + // set target block size to fit within a single block. + encoder + .set_parameter(zstd::stream::raw::CParameter::TargetCBlockSize(1024 * 4)) + .expect("Encoder set_parameter: TargetCBlockSize"); + // do not include the checksum at the end of the encoded data. + encoder + .include_checksum(false) + .expect("Encoder include_checksum: false"); + // do not include magic bytes at the start of the frame since we will have a single + // frame. + encoder + .include_magicbytes(false) + .expect("Encoder include magicbytes: false"); + // set source length, which will be reflected in the frame header. + encoder + .set_pledged_src_size(Some(raw.len() as u64)) + .expect("Encoder src_size: raw.len()"); + // include the content size to know at decode time the expected size of decoded data. + encoder + .include_contentsize(true) + .expect("Encoder include_contentsize: true"); + + encoder.write_all(&raw).expect("Encoder wirte_all"); + encoder.finish().expect("Encoder success") + }; + + println!( + "len(encoded)={:6}\tlen(decoded)={:6}", + compressed.len(), + raw.len() + ); + let k = 18; + let decoder_config_tester: DecoderConfigTester<256, 256> = + DecoderConfigTester { raw, compressed, k }; + let mock_prover = MockProver::::run(k, &decoder_config_tester, vec![]).unwrap(); + mock_prover.assert_satisfied_par(); + + Ok(()) + } } diff --git a/aggregator/src/aggregation/decoder/witgen.rs b/aggregator/src/aggregation/decoder/witgen.rs index 9719e626cd..dc554e9879 100644 --- a/aggregator/src/aggregation/decoder/witgen.rs +++ b/aggregator/src/aggregation/decoder/witgen.rs @@ -83,6 +83,7 @@ fn process_frame_header( _ => fcs, } }; + println!("frame content size = {:?}", fcs); let fcs_tag_value_iter = fcs_bytes .iter() .scan(Value::known(F::zero()), |acc, &byte| { @@ -218,6 +219,13 @@ fn process_block( last_row: &ZstdWitnessRow, randomness: Value, ) -> AggregateBlockResult { + println!( + "process block: block_idx={:?}\tbyte_offset={:?}", + block_idx, byte_offset + ); + if block_idx > 10 { + unreachable!("not more than 10 blocks"); + } let mut witness_rows = vec![]; let (byte_offset, rows, block_info) = @@ -226,7 +234,7 @@ fn process_block( let last_row = rows.last().expect("last row expected to exist"); let ( - _byte_offset, + end_offset, rows, literals, lstream_len, @@ -250,7 +258,7 @@ fn process_block( witness_rows.extend_from_slice(&rows); ( - byte_offset, + end_offset, witness_rows, block_info, sequence_info, @@ -396,7 +404,7 @@ fn process_block_zstd( block_size: usize, last_block: bool, ) -> BlockProcessingResult { - let end_offset = byte_offset + block_size; + let expected_end_offset = byte_offset + block_size; let mut witness_rows = vec![]; // 1-5 bytes LiteralSectionHeader @@ -502,7 +510,7 @@ fn process_block_zstd( let last_row = witness_rows.last().expect("last row expected to exist"); let ( - bytes_offset, + end_offset, rows, fse_aux_tables, address_table_rows, @@ -513,16 +521,17 @@ fn process_block_zstd( src, block_idx, byte_offset, - end_offset, + expected_end_offset, literals.clone(), last_row, last_block, randomness, ); + assert!(end_offset == expected_end_offset, "end offset mismatch"); witness_rows.extend_from_slice(&rows); ( - bytes_offset, + end_offset, witness_rows, literals, lstream_len, @@ -616,7 +625,8 @@ fn process_sequences( // TODO: Treatment of other encoding modes assert!( literal_lengths_mode == 2 || literal_lengths_mode == 0, - "Only FSE_Compressed_Mode is allowed" + "Only FSE_Compressed_Mode is allowed llt_mode={:?}", + literal_lengths_mode, ); assert!( offsets_mode == 2 || offsets_mode == 0, @@ -703,6 +713,8 @@ fn process_sequences( witness_rows.extend_from_slice(&header_rows); + //println!("got seq header (info) = {:?}", sequence_info); + // Second, process the sequence tables (encoded using FSE) let byte_offset = sequence_header_end_offset; let fse_starting_byte_offset = byte_offset; @@ -1997,25 +2009,20 @@ pub fn process(src: &[u8], randomness: Value) -> MultiBlockProcessR let mut address_table_arr: Vec> = vec![]; // TODO: handle multi-block let mut sequence_exec_info_arr: Vec = vec![]; - let byte_offset = 0; // witgen_debug let stdout = io::stdout(); let mut handle = stdout.lock(); // FrameHeaderDescriptor and FrameContentSize - let (byte_offset, rows) = process_frame_header::( - src, - byte_offset, - &ZstdWitnessRow::init(src.len()), - randomness, - ); + let (mut byte_offset, rows) = + process_frame_header::(src, 0, &ZstdWitnessRow::init(src.len()), randomness); witness_rows.extend_from_slice(&rows); let mut block_idx: u64 = 1; loop { let ( - _byte_offset, + end_offset, rows, block_info, sequence_info, @@ -2048,10 +2055,11 @@ pub fn process(src: &[u8], randomness: Value) -> MultiBlockProcessR if block_info.is_last_block { // TODO: Recover this assertion after the sequence section decoding is completed. - // assert!(byte_offset >= src.len()); + assert!(end_offset == src.len()); break; } else { block_idx += 1; + byte_offset = end_offset; } } From f9aa96a9c812f7ac526c33c99b1bf0acc8af481c Mon Sep 17 00:00:00 2001 From: noelwei Date: Fri, 17 May 2024 22:35:34 +0900 Subject: [PATCH 02/15] detour zstd dep Signed-off-by: noelwei --- Cargo.lock | 5 +++++ Cargo.toml | 1 + aggregator/Cargo.toml | 1 + 3 files changed, 7 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index eff4defd92..1f8735e85f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6216,3 +6216,8 @@ dependencies = [ "cc", "pkg-config", ] + +[[patch.unused]] +name = "zstd" +version = "0.13.0" +source = "git+https://github.com/scroll-tech/zstd-rs?branch=test/rohit#9f12f12ea3539228b01f0731311caa6a5c0c34c7" diff --git a/Cargo.toml b/Cargo.toml index 15c492134e..b95124a949 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ ethers-etherscan = { git = "https://github.com/scroll-tech/ethers-rs.git", branc ethers-signers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } gobuild = { git = "https://github.com/scroll-tech/gobuild.git" } halo2curves = { git = "https://github.com/scroll-tech/halo2curves", branch = "v0.1.0" } +zstd = { git = "https://github.com/scroll-tech/zstd-rs", branch = "test/rohit"} [patch."https://github.com/privacy-scaling-explorations/halo2.git"] halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "v1.1" } diff --git a/aggregator/Cargo.toml b/aggregator/Cargo.toml index 2a487b30c0..fb38413700 100644 --- a/aggregator/Cargo.toml +++ b/aggregator/Cargo.toml @@ -37,6 +37,7 @@ revm-primitives = "3.1.0" # da-compression bitstream-io = "2.2.0" zstd = { version = "0.13", features = ["experimental"] } +# zstd = { git = "https://github.com/scroll-tech/zstd-rs", branch = "test/rohit", features = ["experimental"]} [dev-dependencies] From 7ff8fd4d97a46b748be28b5d96baba0ff7579820 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Mon, 20 May 2024 09:38:31 +0100 Subject: [PATCH 03/15] tbt: test for multi-blob --- aggregator/data/test_blobs/multi/202708.hex | Bin 0 -> 113961 bytes aggregator/data/test_blobs/multi/202709.hex | Bin 0 -> 120268 bytes aggregator/data/test_blobs/multi/202710.hex | Bin 0 -> 98471 bytes aggregator/src/aggregation/decoder.rs | 74 +++++++++++++++++++- 4 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 aggregator/data/test_blobs/multi/202708.hex create mode 100644 aggregator/data/test_blobs/multi/202709.hex create mode 100644 aggregator/data/test_blobs/multi/202710.hex diff --git a/aggregator/data/test_blobs/multi/202708.hex b/aggregator/data/test_blobs/multi/202708.hex new file mode 100644 index 0000000000000000000000000000000000000000..55eb68dd7a267a2a5fd01d7328b78d18e22e688d GIT binary patch literal 113961 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 YC)SF4FtzC%uRASeT(P6cjLt>>0}TP*%K!iX literal 0 HcmV?d00001 diff --git a/aggregator/data/test_blobs/multi/202710.hex b/aggregator/data/test_blobs/multi/202710.hex new file mode 100644 index 0000000000000000000000000000000000000000..418021ae9ba0c56c4fe3cd3ffc32fbca1738030f GIT binary patch literal 98471 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%0Dtr0UrQ-_EC2`E{nbXcF2WUwS!PoTu^_rEJO9V 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%0AaC#r}@VlNow=~;nC z)>%E^Q~u@dmstLo0$G7&!7I?I{k_Fhmar*^UAMF0!pDKm7siI|f*W(-%&(y|R@1({ zcaK?@kK$K7o?>z8s|Y3YZpg>n?97Ue=wfE`A(bZpkrzP|Z0*N?m(@1F3aB<6mEV

!=k~kTzD{s7K5lsC2HWP8blC5 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 Result<(), std::io::Error> { + #[ignore = "single_blob: heavy"] + fn test_decoder_config_single_blob() -> Result<(), std::io::Error> { let mut blob_files = fs::read_dir("./data/test_blobs")? .map(|entry| entry.map(|e| e.path())) .collect::, std::io::Error>>()?; @@ -5269,4 +5270,75 @@ mod tests { Ok(()) } + + #[test] + #[ignore = "large_multi_block: heavy"] + fn test_decoder_config_large_multi_block() -> Result<(), std::io::Error> { + let mut batch_files = fs::read_dir("./data/test_blobs/multi")? + .map(|entry| entry.map(|e| e.path())) + .collect::, std::io::Error>>()?; + batch_files.sort(); + + let mut multi_batch_data = Vec::with_capacity(500_000); + for batch_file in batch_files { + let batch_data = fs::read(batch_file)?; + multi_batch_data.extend_from_slice(&batch_data); + } + + let encoded_multi_batch_data = { + // compression level = 0 defaults to using level=3, which is zstd's default. + let mut encoder = + zstd::stream::write::Encoder::new(Vec::new(), 0).expect("Encoder construction"); + + // disable compression of literals, i.e. literals will be raw bytes. + encoder + .set_parameter(zstd::stream::raw::CParameter::LiteralCompressionMode( + zstd::zstd_safe::ParamSwitch::Disable, + )) + .expect("Encoder set_parameter: LiteralCompressionMode"); + // set target block size to fit within a single block. + encoder + .set_parameter(zstd::stream::raw::CParameter::TargetCBlockSize(124 * 1024)) + .expect("Encoder set_parameter: TargetCBlockSize"); + // do not include the checksum at the end of the encoded data. + encoder + .include_checksum(false) + .expect("Encoder include_checksum: false"); + // do not include magic bytes at the start of the frame since we will have a single + // frame. + encoder + .include_magicbytes(false) + .expect("Encoder include magicbytes: false"); + // set source length, which will be reflected in the frame header. + encoder + .set_pledged_src_size(Some(multi_batch_data.len() as u64)) + .expect("Encoder src_size: raw.len()"); + // include the content size to know at decode time the expected size of decoded data. + encoder + .include_contentsize(true) + .expect("Encoder include_contentsize: true"); + + encoder + .write_all(&multi_batch_data) + .expect("Encoder wirte_all"); + encoder.finish().expect("Encoder success") + }; + + println!("len(multi_batch_data)={:?}", multi_batch_data.len()); + println!( + "len(encoded_multi_batch_data)={:?}", + encoded_multi_batch_data.len() + ); + + let k = 20; + let decoder_config_tester: DecoderConfigTester<1024, 512> = DecoderConfigTester { + raw: multi_batch_data, + compressed: encoded_multi_batch_data, + k, + }; + let mock_prover = MockProver::::run(k, &decoder_config_tester, vec![]).unwrap(); + mock_prover.assert_satisfied_par(); + + Ok(()) + } } From 603c2c1737ca9bc068cded90862cd6a5da7833f2 Mon Sep 17 00:00:00 2001 From: noelwei Date: Tue, 21 May 2024 17:26:55 +0900 Subject: [PATCH 04/15] update zstd dep Signed-off-by: noelwei --- Cargo.lock | 27 +++++++++++++++++---------- aggregator/Cargo.toml | 4 ++-- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f8735e85f..8d2992823b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,7 +68,7 @@ dependencies = [ "strum 0.25.0", "strum_macros 0.25.3", "zkevm-circuits", - "zstd 0.13.1", + "zstd 0.13.0", ] [[package]] @@ -6181,11 +6181,10 @@ dependencies = [ [[package]] name = "zstd" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +version = "0.13.0" +source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#dd4facb68b9f4636a4da291a878f23cceb0a47d7" dependencies = [ - "zstd-safe 7.1.0", + "zstd-safe 7.0.0", ] [[package]] @@ -6195,16 +6194,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" dependencies = [ "libc", - "zstd-sys", + "zstd-sys 2.0.10+zstd.1.5.6", ] [[package]] name = "zstd-safe" -version = "7.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +version = "7.0.0" +source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#dd4facb68b9f4636a4da291a878f23cceb0a47d7" dependencies = [ - "zstd-sys", + "zstd-sys 2.0.9+zstd.1.5.5", +] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" +source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#dd4facb68b9f4636a4da291a878f23cceb0a47d7" +dependencies = [ + "cc", + "pkg-config", ] [[package]] diff --git a/aggregator/Cargo.toml b/aggregator/Cargo.toml index fb38413700..21954dc30e 100644 --- a/aggregator/Cargo.toml +++ b/aggregator/Cargo.toml @@ -36,8 +36,8 @@ revm-primitives = "3.1.0" # da-compression bitstream-io = "2.2.0" -zstd = { version = "0.13", features = ["experimental"] } -# zstd = { git = "https://github.com/scroll-tech/zstd-rs", branch = "test/rohit", features = ["experimental"]} +# zstd = { version = "0.13", features = ["experimental"] } +zstd = { git = "https://github.com/scroll-tech/zstd-rs", branch = "hack/mul-block", features = ["experimental"]} [dev-dependencies] From 83c6e821793b1189aadfb226ead0db8aa62afe7a Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Tue, 21 May 2024 09:32:49 +0100 Subject: [PATCH 05/15] chore: remove unused patch --- Cargo.lock | 5 ----- Cargo.toml | 1 - aggregator/Cargo.toml | 1 - 3 files changed, 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d2992823b..1a42ff0eee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6223,8 +6223,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[patch.unused]] -name = "zstd" -version = "0.13.0" -source = "git+https://github.com/scroll-tech/zstd-rs?branch=test/rohit#9f12f12ea3539228b01f0731311caa6a5c0c34c7" diff --git a/Cargo.toml b/Cargo.toml index b95124a949..15c492134e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,7 +73,6 @@ ethers-etherscan = { git = "https://github.com/scroll-tech/ethers-rs.git", branc ethers-signers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } gobuild = { git = "https://github.com/scroll-tech/gobuild.git" } halo2curves = { git = "https://github.com/scroll-tech/halo2curves", branch = "v0.1.0" } -zstd = { git = "https://github.com/scroll-tech/zstd-rs", branch = "test/rohit"} [patch."https://github.com/privacy-scaling-explorations/halo2.git"] halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "v1.1" } diff --git a/aggregator/Cargo.toml b/aggregator/Cargo.toml index 21954dc30e..d1fb6b7a6d 100644 --- a/aggregator/Cargo.toml +++ b/aggregator/Cargo.toml @@ -36,7 +36,6 @@ revm-primitives = "3.1.0" # da-compression bitstream-io = "2.2.0" -# zstd = { version = "0.13", features = ["experimental"] } zstd = { git = "https://github.com/scroll-tech/zstd-rs", branch = "hack/mul-block", features = ["experimental"]} [dev-dependencies] From 197295d13f4b6b7c6721b7d058e173bff8a909f9 Mon Sep 17 00:00:00 2001 From: noelwei Date: Wed, 22 May 2024 00:44:10 +0900 Subject: [PATCH 06/15] update zstd (scroll repo) Signed-off-by: noelwei --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a42ff0eee..3ccc98b5a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6182,7 +6182,7 @@ dependencies = [ [[package]] name = "zstd" version = "0.13.0" -source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#dd4facb68b9f4636a4da291a878f23cceb0a47d7" +source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#7e243272c5483ced832ff72616b8211c77978521" dependencies = [ "zstd-safe 7.0.0", ] @@ -6200,7 +6200,7 @@ dependencies = [ [[package]] name = "zstd-safe" version = "7.0.0" -source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#dd4facb68b9f4636a4da291a878f23cceb0a47d7" +source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#7e243272c5483ced832ff72616b8211c77978521" dependencies = [ "zstd-sys 2.0.9+zstd.1.5.5", ] @@ -6208,7 +6208,7 @@ dependencies = [ [[package]] name = "zstd-sys" version = "2.0.9+zstd.1.5.5" -source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#dd4facb68b9f4636a4da291a878f23cceb0a47d7" +source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#7e243272c5483ced832ff72616b8211c77978521" dependencies = [ "cc", "pkg-config", From 0f3785a39b701220cfa5061b09ac1bdd9294d6de Mon Sep 17 00:00:00 2001 From: Ray Gao Date: Wed, 22 May 2024 05:02:30 -0400 Subject: [PATCH 07/15] Correct multi-block back reference target bytes (#1275) Co-authored-by: Rohit Narurkar --- aggregator/src/aggregation/decoder/witgen.rs | 37 ++++++++++---------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/aggregator/src/aggregation/decoder/witgen.rs b/aggregator/src/aggregation/decoder/witgen.rs index a80976d4cf..c8ef381c03 100644 --- a/aggregator/src/aggregation/decoder/witgen.rs +++ b/aggregator/src/aggregation/decoder/witgen.rs @@ -10,6 +10,7 @@ pub use types::{ZstdTag::*, *}; pub mod util; use util::{be_bits_to_value, increment_idx, le_bits_to_value, value_bits_le}; +use zstd::zstd_safe::WriteBuf; const CMOT_N: u64 = 31; @@ -142,6 +143,7 @@ type AggregateBlockResult = ( ); fn process_block( src: &[u8], + decoded_bytes: &mut Vec, block_idx: u64, byte_offset: usize, last_row: &ZstdWitnessRow, @@ -174,6 +176,7 @@ fn process_block( ) = match block_info.block_type { BlockType::ZstdCompressedBlock => process_block_zstd( src, + decoded_bytes, block_idx, byte_offset, last_row, @@ -297,6 +300,7 @@ type LiteralsBlockResult = (usize, Vec>, Vec, Vec #[allow(unused_variables)] fn process_block_zstd( src: &[u8], + decoded_bytes: &mut Vec, block_idx: u64, byte_offset: usize, last_row: &ZstdWitnessRow, @@ -403,6 +407,7 @@ fn process_block_zstd( sequence_exec_info, ) = process_sequences::( src, + decoded_bytes, block_idx, byte_offset, expected_end_offset, @@ -457,6 +462,7 @@ type SequencesProcessingResult = ( #[allow(clippy::too_many_arguments)] fn process_sequences( src: &[u8], + decoded_bytes: &mut Vec, block_idx: u64, byte_offset: usize, end_offset: usize, @@ -1561,16 +1567,12 @@ fn process_sequences( inst.instruction_idx as usize, SequenceExecInfo::LiteralCopy(r.clone()), )); - recovered_inputs.extend_from_slice( - literals[r] - .iter() - .map(|&v| v as u8) - .collect::>() - .as_slice(), - ); + let ext_slice = literals[r].iter().map(|&v| v as u8).collect::>(); + recovered_inputs.extend_from_slice(ext_slice.as_slice()); + decoded_bytes.extend_from_slice(ext_slice.as_slice()); } - let match_pos = recovered_inputs.len() - (inst.actual_offset as usize); + let match_pos = decoded_bytes.len() - (inst.actual_offset as usize); if inst.match_length > 0 { let r = match_pos..(inst.match_length as usize + match_pos); seq_exec_info.push(SequenceExec( @@ -1578,14 +1580,15 @@ fn process_sequences( SequenceExecInfo::BackRef(r.clone()), )); let matched_and_repeated_bytes = if inst.match_length <= inst.actual_offset { - Vec::from(&recovered_inputs[r]) + Vec::from(&decoded_bytes[r]) } else { let l = inst.match_length as usize; - let r_prime = match_pos..recovered_inputs.len(); - let matched_bytes = Vec::from(&recovered_inputs[r_prime]); + let r_prime = match_pos..decoded_bytes.len(); + let matched_bytes = Vec::from(&decoded_bytes[r_prime]); matched_bytes.iter().cycle().take(l).copied().collect() }; recovered_inputs.extend_from_slice(matched_and_repeated_bytes.as_slice()); + decoded_bytes.extend_from_slice(matched_and_repeated_bytes.as_slice()); } current_literal_pos = new_literal_pos; } @@ -1597,13 +1600,9 @@ fn process_sequences( sequence_info.num_sequences, SequenceExecInfo::LiteralCopy(r.clone()), )); - recovered_inputs.extend_from_slice( - literals[r] - .iter() - .map(|&v| v as u8) - .collect::>() - .as_slice(), - ); + let ext_slice = literals[r].iter().map(|&v| v as u8).collect::>(); + recovered_inputs.extend_from_slice(ext_slice.as_slice()); + decoded_bytes.extend_from_slice(ext_slice.as_slice()); } ( @@ -1741,6 +1740,7 @@ pub type MultiBlockProcessResult = ( /// Process a slice of bytes into decompression circuit witness rows pub fn process(src: &[u8], randomness: Value) -> MultiBlockProcessResult { let mut witness_rows = vec![]; + let mut decoded_bytes: Vec = vec![]; let mut literals: Vec> = vec![]; let mut aux_data: Vec = vec![]; let mut fse_aux_tables: Vec = vec![]; @@ -1775,6 +1775,7 @@ pub fn process(src: &[u8], randomness: Value) -> MultiBlockProcessR sequence_exec_info, ) = process_block::( src, + &mut decoded_bytes, block_idx, byte_offset, rows.last().expect("last row expected to exist"), From d3a05b2b85f8a9854a0b9d5b96e33c2b76dad811 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Wed, 22 May 2024 10:57:32 +0100 Subject: [PATCH 08/15] minor fix in assignment (block_info and seq_info) --- aggregator/src/aggregation/decoder.rs | 83 +++----------------- aggregator/src/aggregation/decoder/witgen.rs | 1 - 2 files changed, 12 insertions(+), 72 deletions(-) diff --git a/aggregator/src/aggregation/decoder.rs b/aggregator/src/aggregation/decoder.rs index c1a57a4ce5..2f7e68fdf2 100644 --- a/aggregator/src/aggregation/decoder.rs +++ b/aggregator/src/aggregation/decoder.rs @@ -4150,15 +4150,6 @@ impl DecoderConfig { ) -> Result { let mut pow_of_rand: Vec> = vec![Value::known(Fr::ONE)]; - assert!(!block_info_arr.is_empty(), "Must have at least 1 block"); - assert!(!sequence_info_arr.is_empty(), "Must have at least 1 block"); - - assert!(address_table_arr.len() == 1, "TODO: multi-block"); - assert!(sequence_exec_info_arr.len() == 1, "TODO: multi-block"); - - let mut curr_block_info = block_info_arr[0]; - let mut curr_sequence_info = sequence_info_arr[0]; - ///////////////////////////////////////// //////// Load Auxiliary Tables ///////// ///////////////////////////////////////// @@ -4545,18 +4536,16 @@ impl DecoderConfig { let is_block_header = row.state.tag == BlockHeader; if is_block || is_block_header { - if block_idx != curr_block_info.block_idx as u64 { - curr_block_info = *block_info_arr - .iter() - .find(|&b| b.block_idx == block_idx as usize) - .expect("Block info should exist"); - } - if block_idx != curr_sequence_info.block_idx as u64 { - curr_sequence_info = *sequence_info_arr - .iter() - .find(|&s| s.block_idx == block_idx as usize) - .expect("Sequence info should exist"); - } + let curr_block_info = block_info_arr[block_idx as usize - 1]; + assert_eq!( + block_idx as usize, curr_block_info.block_idx, + "block_idx mismatch" + ); + let curr_sequence_info = sequence_info_arr[block_idx as usize - 1]; + assert_eq!( + block_idx as usize, curr_sequence_info.block_idx, + "block_idx mismatch" + ); region.assign_advice( || "block_config.block_len", self.block_config.block_len, @@ -5176,36 +5165,12 @@ mod tests { let raw = batches[1].clone(); let compressed = { // compression level = 0 defaults to using level=3, which is zstd's default. - let mut encoder = - zstd::stream::write::Encoder::new(Vec::new(), 0).expect("Encoder construction"); + let mut encoder = init_zstd_encoder(Some(1024 * 4)); - // disable compression of literals, i.e. literals will be raw bytes. - encoder - .set_parameter(zstd::stream::raw::CParameter::LiteralCompressionMode( - zstd::zstd_safe::ParamSwitch::Disable, - )) - .expect("Encoder set_parameter: LiteralCompressionMode"); - // set target block size to fit within a single block. - encoder - .set_parameter(zstd::stream::raw::CParameter::TargetCBlockSize(1024 * 4)) - .expect("Encoder set_parameter: TargetCBlockSize"); - // do not include the checksum at the end of the encoded data. - encoder - .include_checksum(false) - .expect("Encoder include_checksum: false"); - // do not include magic bytes at the start of the frame since we will have a single - // frame. - encoder - .include_magicbytes(false) - .expect("Encoder include magicbytes: false"); // set source length, which will be reflected in the frame header. encoder .set_pledged_src_size(Some(raw.len() as u64)) .expect("Encoder src_size: raw.len()"); - // include the content size to know at decode time the expected size of decoded data. - encoder - .include_contentsize(true) - .expect("Encoder include_contentsize: true"); encoder.write_all(&raw).expect("Encoder wirte_all"); encoder.finish().expect("Encoder success") @@ -5241,36 +5206,12 @@ mod tests { let encoded_multi_batch_data = { // compression level = 0 defaults to using level=3, which is zstd's default. - let mut encoder = - zstd::stream::write::Encoder::new(Vec::new(), 0).expect("Encoder construction"); + let mut encoder = init_zstd_encoder(None); - // disable compression of literals, i.e. literals will be raw bytes. - encoder - .set_parameter(zstd::stream::raw::CParameter::LiteralCompressionMode( - zstd::zstd_safe::ParamSwitch::Disable, - )) - .expect("Encoder set_parameter: LiteralCompressionMode"); - // set target block size to fit within a single block. - encoder - .set_parameter(zstd::stream::raw::CParameter::TargetCBlockSize(124 * 1024)) - .expect("Encoder set_parameter: TargetCBlockSize"); - // do not include the checksum at the end of the encoded data. - encoder - .include_checksum(false) - .expect("Encoder include_checksum: false"); - // do not include magic bytes at the start of the frame since we will have a single - // frame. - encoder - .include_magicbytes(false) - .expect("Encoder include magicbytes: false"); // set source length, which will be reflected in the frame header. encoder .set_pledged_src_size(Some(multi_batch_data.len() as u64)) .expect("Encoder src_size: raw.len()"); - // include the content size to know at decode time the expected size of decoded data. - encoder - .include_contentsize(true) - .expect("Encoder include_contentsize: true"); encoder .write_all(&multi_batch_data) diff --git a/aggregator/src/aggregation/decoder/witgen.rs b/aggregator/src/aggregation/decoder/witgen.rs index c8ef381c03..161b3c1e66 100644 --- a/aggregator/src/aggregation/decoder/witgen.rs +++ b/aggregator/src/aggregation/decoder/witgen.rs @@ -593,7 +593,6 @@ fn process_sequences( witness_rows.extend_from_slice(&header_rows); - ///////////////////////////////////////////////// ///// Sequence Section Part 2: FSE Tables ////// ///////////////////////////////////////////////// From 3f396e33604258c0557f7d152600b80f3509841e Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Wed, 22 May 2024 12:06:45 +0100 Subject: [PATCH 09/15] dbg: fix some issues --- .../tables/fixed/fse_table_transition.rs | 37 ++++----- .../decoder/tables/fixed/tag_transition.rs | 1 + aggregator/src/aggregation/decoder/witgen.rs | 76 +++++++------------ .../src/aggregation/decoder/witgen/params.rs | 3 + 4 files changed, 49 insertions(+), 68 deletions(-) diff --git a/aggregator/src/aggregation/decoder/tables/fixed/fse_table_transition.rs b/aggregator/src/aggregation/decoder/tables/fixed/fse_table_transition.rs index a34c1bbca6..83a688c683 100644 --- a/aggregator/src/aggregation/decoder/tables/fixed/fse_table_transition.rs +++ b/aggregator/src/aggregation/decoder/tables/fixed/fse_table_transition.rs @@ -1,7 +1,5 @@ use halo2_proofs::{circuit::Value, halo2curves::bn256::Fr}; -use crate::aggregation::decoder::witgen::FseTableKind; - use super::{FixedLookupTag, FixedLookupValues}; pub struct RomFseTableTransition { @@ -17,36 +15,33 @@ pub struct RomFseTableTransition { impl FixedLookupValues for RomFseTableTransition { fn values() -> Vec<[Value; 7]> { - [ - vec![[ - Value::known(Fr::from(FixedLookupTag::FseTableTransition as u64)), - Value::known(Fr::zero()), // block_idx_prev - Value::known(Fr::one()), // block_idx_curr - Value::known(Fr::zero()), // table_kind_prev - Value::known(Fr::from(FseTableKind::LLT as u64)), - Value::known(Fr::zero()), - Value::known(Fr::zero()), - ]], - [ - (1, 1, FseTableKind::LLT, FseTableKind::MOT), - (1, 1, FseTableKind::MOT, FseTableKind::MLT), - // TODO: add more for multi-block scenario - ] + use crate::witgen::{ + FseTableKind::{LLT, MLT, MOT}, + N_MAX_BLOCKS, + }; + + (1..N_MAX_BLOCKS) + .flat_map(|block_idx_curr| { + let table_kind_prev = if block_idx_curr == 1 { None } else { Some(MLT) }; + [ + (block_idx_curr - 1, block_idx_curr, table_kind_prev, LLT), + (block_idx_curr, block_idx_curr, Some(LLT), MOT), + (block_idx_curr, block_idx_curr, Some(MOT), MLT), + ] + }) .map( |(block_idx_prev, block_idx_curr, table_kind_prev, table_kind_curr)| { [ Value::known(Fr::from(FixedLookupTag::FseTableTransition as u64)), Value::known(Fr::from(block_idx_prev)), Value::known(Fr::from(block_idx_curr)), - Value::known(Fr::from(table_kind_prev as u64)), + Value::known(table_kind_prev.map_or(Fr::zero(), |v| Fr::from(v as u64))), Value::known(Fr::from(table_kind_curr as u64)), Value::known(Fr::zero()), Value::known(Fr::zero()), ] }, ) - .to_vec(), - ] - .concat() + .collect() } } diff --git a/aggregator/src/aggregation/decoder/tables/fixed/tag_transition.rs b/aggregator/src/aggregation/decoder/tables/fixed/tag_transition.rs index ac1272281c..3e90910e7a 100644 --- a/aggregator/src/aggregation/decoder/tables/fixed/tag_transition.rs +++ b/aggregator/src/aggregation/decoder/tables/fixed/tag_transition.rs @@ -35,6 +35,7 @@ impl FixedLookupValues for RomTagTransition { (ZstdBlockSequenceHeader, ZstdBlockSequenceData), (ZstdBlockSequenceFseCode, ZstdBlockSequenceFseCode), (ZstdBlockSequenceFseCode, ZstdBlockSequenceData), + (ZstdBlockSequenceData, BlockHeader), // multi-block (ZstdBlockSequenceData, Null), (Null, Null), ] diff --git a/aggregator/src/aggregation/decoder/witgen.rs b/aggregator/src/aggregation/decoder/witgen.rs index 161b3c1e66..bb9c4ab589 100644 --- a/aggregator/src/aggregation/decoder/witgen.rs +++ b/aggregator/src/aggregation/decoder/witgen.rs @@ -10,7 +10,6 @@ pub use types::{ZstdTag::*, *}; pub mod util; use util::{be_bits_to_value, increment_idx, le_bits_to_value, value_bits_le}; -use zstd::zstd_safe::WriteBuf; const CMOT_N: u64 = 31; @@ -153,9 +152,6 @@ fn process_block( "process block: block_idx={:?}\tbyte_offset={:?}", block_idx, byte_offset ); - if block_idx > 10 { - unreachable!("not more than 10 blocks"); - } let mut witness_rows = vec![]; let (byte_offset, rows, block_info) = @@ -297,7 +293,7 @@ type BlockProcessingResult = ( type LiteralsBlockResult = (usize, Vec>, Vec, Vec, Vec); -#[allow(unused_variables)] +#[allow(clippy::too_many_arguments)] fn process_block_zstd( src: &[u8], decoded_bytes: &mut Vec, @@ -318,7 +314,7 @@ fn process_block_zstd( byte_offset, rows, _literals_block_type, - n_streams, + _n_streams, regen_size, compressed_size, (branch, sf_max), @@ -334,58 +330,44 @@ fn process_block_zstd( let tag = ZstdTag::ZstdBlockLiteralsRawBytes; let tag_next = ZstdTag::ZstdBlockSequenceHeader; let literals = src[byte_offset..(byte_offset + regen_size)].to_vec(); - let value_rlc_iter = literals - .iter() - .scan(last_row.encoded_data.value_rlc, |acc, &byte| { - *acc = *acc * randomness + Value::known(F::from(byte as u64)); - Some(*acc) - }); - let tag_value_iter = literals.iter().scan(Value::known(F::zero()), |acc, &byte| { - *acc = *acc * randomness + Value::known(F::from(byte as u64)); - Some(*acc) - }); - let tag_value = tag_value_iter.clone().last().expect("Literals must exist."); let tag_rlc_iter = literals.iter().scan(Value::known(F::zero()), |acc, &byte| { *acc = *acc * randomness + Value::known(F::from(byte as u64)); Some(*acc) }); - let tag_rlc = tag_value_iter.clone().last().expect("Literals must exist."); + let tag_rlc = tag_rlc_iter.clone().last().expect("Literals must exist."); ( byte_offset + regen_size, literals .iter() - .zip(tag_value_iter) .zip(tag_rlc_iter) .enumerate() - .map( - |(i, ((&value_byte, tag_value_acc), tag_rlc_acc))| ZstdWitnessRow { - state: ZstdState { - tag, - tag_next, - block_idx, - max_tag_len: tag.max_len(), - tag_len: regen_size as u64, - tag_idx: (i + 1) as u64, - is_tag_change: i == 0, - tag_rlc, - tag_rlc_acc, - }, - encoded_data: EncodedData { - byte_idx: (byte_offset + i + 1) as u64, - encoded_len: last_row.encoded_data.encoded_len, - value_byte, - value_rlc, - reverse: false, - ..Default::default() - }, - decoded_data: DecodedData { - decoded_len: last_row.decoded_data.decoded_len, - }, - bitstream_read_data: BitstreamReadRow::default(), - fse_data: FseDecodingRow::default(), + .map(|(i, (&value_byte, tag_rlc_acc))| ZstdWitnessRow { + state: ZstdState { + tag, + tag_next, + block_idx, + max_tag_len: tag.max_len(), + tag_len: regen_size as u64, + tag_idx: (i + 1) as u64, + is_tag_change: i == 0, + tag_rlc, + tag_rlc_acc, }, - ) + encoded_data: EncodedData { + byte_idx: (byte_offset + i + 1) as u64, + encoded_len: last_row.encoded_data.encoded_len, + value_byte, + value_rlc, + reverse: false, + ..Default::default() + }, + decoded_data: DecodedData { + decoded_len: last_row.decoded_data.decoded_len, + }, + bitstream_read_data: BitstreamReadRow::default(), + fse_data: FseDecodingRow::default(), + }) .collect::>(), literals.iter().map(|b| *b as u64).collect::>(), vec![regen_size as u64, 0, 0, 0], @@ -1777,7 +1759,7 @@ pub fn process(src: &[u8], randomness: Value) -> MultiBlockProcessR &mut decoded_bytes, block_idx, byte_offset, - rows.last().expect("last row expected to exist"), + witness_rows.last().expect("last row expected to exist"), randomness, ); diff --git a/aggregator/src/aggregation/decoder/witgen/params.rs b/aggregator/src/aggregation/decoder/witgen/params.rs index a10e6e99c9..6b7e1ba7bd 100644 --- a/aggregator/src/aggregation/decoder/witgen/params.rs +++ b/aggregator/src/aggregation/decoder/witgen/params.rs @@ -21,6 +21,9 @@ pub const CL_WINDOW_LIMIT: usize = 17; /// zstd block size target. pub const N_BLOCK_SIZE_TARGET: u32 = 124 * 1024; +/// Maximum number of blocks that we can expect in the encoded data. +pub const N_MAX_BLOCKS: u64 = 10; + /// Zstd encoder configuration pub fn init_zstd_encoder( target_block_size: Option, From 8f34d2421cc24bbb3be09307f7071afc62aacef7 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Wed, 22 May 2024 12:20:33 +0100 Subject: [PATCH 10/15] dbg: fix bitstring table lookup for byte_idx delta --- aggregator/src/aggregation/config.rs | 2 ++ aggregator/src/aggregation/decoder.rs | 11 ++++++++++- .../aggregation/decoder/tables/bitstring.rs | 19 ++++++++++--------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/aggregator/src/aggregation/config.rs b/aggregator/src/aggregation/config.rs index 3aec9ff7d5..f4765ad716 100644 --- a/aggregator/src/aggregation/config.rs +++ b/aggregator/src/aggregation/config.rs @@ -131,6 +131,7 @@ impl AggregationConfig { let range8 = RangeTable::construct(meta); let range16 = RangeTable::construct(meta); let range512 = RangeTable::construct(meta); + let range_block_len = RangeTable::construct(meta); let bitwise_op_table = BitwiseOpTable::construct(meta); let decoder_config = DecoderConfig::configure( meta, @@ -142,6 +143,7 @@ impl AggregationConfig { range8, range16, range512, + range_block_len, bitwise_op_table, }, ); diff --git a/aggregator/src/aggregation/decoder.rs b/aggregator/src/aggregation/decoder.rs index 2f7e68fdf2..2717ff723e 100644 --- a/aggregator/src/aggregation/decoder.rs +++ b/aggregator/src/aggregation/decoder.rs @@ -81,6 +81,8 @@ pub struct DecoderConfig { range16: RangeTable<16>, /// Range Table for [0, 512). range512: RangeTable<512>, + /// Range table for [0, 128kb). + range_block_len: RangeTable<{ N_BLOCK_SIZE_TARGET as usize }>, /// Power of 2 lookup table. pow2_table: Pow2Table<20>, /// Helper table for decoding the regenerated size from LiteralsHeader. @@ -961,6 +963,8 @@ pub struct DecoderConfigArgs { pub range16: RangeTable<16>, /// Range table for lookup: [0, 512). pub range512: RangeTable<512>, + /// Range table for [0, 128kb). + pub range_block_len: RangeTable<{ N_BLOCK_SIZE_TARGET as usize }>, /// Bitwise operation lookup table. pub bitwise_op_table: BitwiseOpTable<1, L, R>, } @@ -976,6 +980,7 @@ impl DecoderConfig { range8, range16, range512, + range_block_len, bitwise_op_table, }: DecoderConfigArgs, ) -> Self { @@ -991,7 +996,7 @@ impl DecoderConfig { ); // Helper tables let literals_header_table = LiteralsHeaderTable::configure(meta, q_enable, range8, range16); - let bitstring_table = BitstringTable::configure(meta, q_enable, u8_table); + let bitstring_table = BitstringTable::configure(meta, q_enable, range_block_len); let fse_table = FseTable::configure( meta, q_enable, @@ -1063,6 +1068,7 @@ impl DecoderConfig { range8, range16, range512, + range_block_len, pow2_table, literals_header_table, bitstring_table, @@ -4156,6 +4162,7 @@ impl DecoderConfig { self.range8.load(layouter)?; self.range16.load(layouter)?; self.range512.load(layouter)?; + self.range_block_len.load(layouter)?; self.fixed_table.load(layouter)?; self.pow2_table.load(layouter)?; @@ -4918,6 +4925,7 @@ mod tests { let range8 = RangeTable::construct(meta); let range16 = RangeTable::construct(meta); let range512 = RangeTable::construct(meta); + let range_block_len = RangeTable::construct(meta); let bitwise_op_table = BitwiseOpTable::construct(meta); let config = DecoderConfig::configure( @@ -4930,6 +4938,7 @@ mod tests { range8, range16, range512, + range_block_len, bitwise_op_table, }, ); diff --git a/aggregator/src/aggregation/decoder/tables/bitstring.rs b/aggregator/src/aggregation/decoder/tables/bitstring.rs index 0f5b38927a..757c69c1c3 100644 --- a/aggregator/src/aggregation/decoder/tables/bitstring.rs +++ b/aggregator/src/aggregation/decoder/tables/bitstring.rs @@ -7,13 +7,16 @@ use halo2_proofs::{ }; use zkevm_circuits::{ evm_circuit::{BaseConstraintBuilder, ConstrainBuilderCommon}, - table::{LookupTable, U8Table}, + table::{LookupTable, RangeTable}, }; -use crate::aggregation::decoder::{ - util::value_bits_le, - witgen::{ZstdTag, ZstdWitnessRow}, - BlockInfo, +use crate::{ + aggregation::decoder::{ + util::value_bits_le, + witgen::{ZstdTag, ZstdWitnessRow}, + BlockInfo, + }, + witgen::N_BLOCK_SIZE_TARGET, }; /// In the process of decoding zstd encoded data, there are several scenarios in which we process @@ -111,7 +114,7 @@ impl BitstringTable { pub fn configure( meta: &mut ConstraintSystem, q_enable: Column, - u8_table: U8Table, + range_block_len: RangeTable<{ N_BLOCK_SIZE_TARGET as usize }>, ) -> Self { let config = Self { q_first: meta.fixed_column(), @@ -433,8 +436,6 @@ impl BitstringTable { // However, we still want to make sure subsequent bitstring accumulation happens in // increasing order of byte indices, to avoid malicious assignments for an older byte // index. We need this check only for subsequent bitstrings after q_first=true. - // - // TODO: for a multi-block setup, the difference may be greater than 255. meta.lookup("BitstringTable: byte_idx_1 is increasing", |meta| { let condition = and::expr([ meta.query_fixed(q_enable, Rotation::cur()), @@ -449,7 +450,7 @@ impl BitstringTable { ); let byte_idx_delta = byte_idx_1_curr - byte_idx_1_prev; - vec![(condition * byte_idx_delta, u8_table.into())] + vec![(condition * byte_idx_delta, range_block_len.into())] }); debug_assert!(meta.degree() <= 9); From 353d5d61a385693c76a989806e006ec73bb8784a Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Wed, 22 May 2024 13:42:18 +0100 Subject: [PATCH 11/15] fix: repeated offsets are carried forward into next block --- aggregator/src/aggregation/decoder/witgen.rs | 24 ++++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/aggregator/src/aggregation/decoder/witgen.rs b/aggregator/src/aggregation/decoder/witgen.rs index bb9c4ab589..3a376eada7 100644 --- a/aggregator/src/aggregation/decoder/witgen.rs +++ b/aggregator/src/aggregation/decoder/witgen.rs @@ -61,8 +61,6 @@ fn process_frame_header( } }; - println!("frame content size = {:?}", fcs); - let tag_rlc_iter = fcs_bytes .iter() .scan(Value::known(F::zero()), |acc, &byte| { @@ -139,6 +137,7 @@ type AggregateBlockResult = ( [FseAuxiliaryTableData; 3], // 3 sequence section FSE tables Vec, SequenceExecResult, + [usize; 3], // repeated offsets are carried forward between blocks. ); fn process_block( src: &[u8], @@ -147,11 +146,8 @@ fn process_block( byte_offset: usize, last_row: &ZstdWitnessRow, randomness: Value, + repeated_offset: [usize; 3], ) -> AggregateBlockResult { - println!( - "process block: block_idx={:?}\tbyte_offset={:?}", - block_idx, byte_offset - ); let mut witness_rows = vec![]; let (byte_offset, rows, block_info) = @@ -169,6 +165,7 @@ fn process_block( fse_aux_tables, address_table_rows, sequence_exec_info, + repeated_offset, ) = match block_info.block_type { BlockType::ZstdCompressedBlock => process_block_zstd( src, @@ -179,6 +176,7 @@ fn process_block( randomness, block_info.block_len, block_info.is_last_block, + repeated_offset, ), _ => unreachable!("BlockType::ZstdCompressedBlock expected"), }; @@ -195,6 +193,7 @@ fn process_block( fse_aux_tables, address_table_rows, sequence_exec_info, + repeated_offset, ) } @@ -289,6 +288,7 @@ type BlockProcessingResult = ( [FseAuxiliaryTableData; 3], // 3 sequence section FSE tables Vec, SequenceExecResult, + [usize; 3], // repeated offsets are carried forward between blocks ); type LiteralsBlockResult = (usize, Vec>, Vec, Vec, Vec); @@ -303,6 +303,7 @@ fn process_block_zstd( randomness: Value, block_size: usize, last_block: bool, + repeated_offset: [usize; 3], ) -> BlockProcessingResult { let expected_end_offset = byte_offset + block_size; let mut witness_rows = vec![]; @@ -387,6 +388,7 @@ fn process_block_zstd( original_inputs, sequence_info, sequence_exec_info, + repeated_offset, ) = process_sequences::( src, decoded_bytes, @@ -397,6 +399,7 @@ fn process_block_zstd( last_row, last_block, randomness, + repeated_offset, ); // sanity check: @@ -428,6 +431,7 @@ fn process_block_zstd( exec_trace: sequence_exec_info, recovered_bytes: original_inputs, }, + repeated_offset, ) } @@ -439,6 +443,7 @@ type SequencesProcessingResult = ( Vec, // Recovered original input SequenceInfo, Vec, + [usize; 3], // repeated offsets are carried forward. ); #[allow(clippy::too_many_arguments)] @@ -452,6 +457,7 @@ fn process_sequences( last_row: &ZstdWitnessRow, last_block: bool, randomness: Value, + mut repeated_offset: [usize; 3], ) -> SequencesProcessingResult { // Initialize witness values let mut witness_rows: Vec> = vec![]; @@ -1470,7 +1476,6 @@ fn process_sequences( // Process raw sequence instructions let mut address_table_rows: Vec = vec![]; let mut literal_len_acc: usize = 0; - let mut repeated_offset: [usize; 3] = [1, 4, 8]; for (idx, inst) in raw_sequence_instructions.iter().enumerate() { let actual_offset = if inst.0 > 3 { @@ -1594,6 +1599,7 @@ fn process_sequences( recovered_inputs, sequence_info, seq_exec_info, + repeated_offset, ) } @@ -1742,6 +1748,7 @@ pub fn process(src: &[u8], randomness: Value) -> MultiBlockProcessR witness_rows.extend_from_slice(&rows); let mut block_idx: u64 = 1; + let mut repeated_offset = [1, 4, 8]; loop { let ( end_offset, @@ -1754,6 +1761,7 @@ pub fn process(src: &[u8], randomness: Value) -> MultiBlockProcessR new_fse_aux_tables, address_table_rows, sequence_exec_info, + end_repeated_offset, ) = process_block::( src, &mut decoded_bytes, @@ -1761,6 +1769,7 @@ pub fn process(src: &[u8], randomness: Value) -> MultiBlockProcessR byte_offset, witness_rows.last().expect("last row expected to exist"), randomness, + repeated_offset, ); witness_rows.extend_from_slice(&rows); @@ -1780,6 +1789,7 @@ pub fn process(src: &[u8], randomness: Value) -> MultiBlockProcessR assert!(end_offset >= src.len()); break; } else { + repeated_offset = end_repeated_offset; block_idx += 1; byte_offset = end_offset; } From b700971bc0af9a2e24f885bdbcb88aaff9ecf7e0 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Wed, 22 May 2024 13:57:20 +0100 Subject: [PATCH 12/15] wip test: large-multi-block --- aggregator/src/aggregation/decoder.rs | 1 - aggregator/src/aggregation/decoder/witgen.rs | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aggregator/src/aggregation/decoder.rs b/aggregator/src/aggregation/decoder.rs index 2717ff723e..9020f42865 100644 --- a/aggregator/src/aggregation/decoder.rs +++ b/aggregator/src/aggregation/decoder.rs @@ -5200,7 +5200,6 @@ mod tests { } #[test] - #[ignore = "large_multi_block: heavy"] fn test_decoder_config_large_multi_block() -> Result<(), std::io::Error> { let mut batch_files = fs::read_dir("./data/test_blobs/multi")? .map(|entry| entry.map(|e| e.path())) diff --git a/aggregator/src/aggregation/decoder/witgen.rs b/aggregator/src/aggregation/decoder/witgen.rs index 3a376eada7..bef8f2dd43 100644 --- a/aggregator/src/aggregation/decoder/witgen.rs +++ b/aggregator/src/aggregation/decoder/witgen.rs @@ -1355,7 +1355,7 @@ fn process_sequences( let wrap_by = match to_bit_idx { 15 => 8, 16..=23 => 16, - _ => unreachable!(), + v => unreachable!("unexpected bitstring-length={:?}", v), }; witness_rows.push(ZstdWitnessRow { state: ZstdState { @@ -1771,6 +1771,7 @@ pub fn process(src: &[u8], randomness: Value) -> MultiBlockProcessR randomness, repeated_offset, ); + println!("processed block={:?}: offset={:?}", block_idx, end_offset); witness_rows.extend_from_slice(&rows); literals.push(new_literals); From 65b90cf775c6821f3ee3a717f3f923dc9fb3be38 Mon Sep 17 00:00:00 2001 From: noelwei Date: Thu, 23 May 2024 00:29:25 +0900 Subject: [PATCH 13/15] update zstd, set windowlog Signed-off-by: noelwei --- Cargo.lock | 6 +++--- aggregator/src/aggregation/decoder/witgen/params.rs | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ccc98b5a4..8aeea4033b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6182,7 +6182,7 @@ dependencies = [ [[package]] name = "zstd" version = "0.13.0" -source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#7e243272c5483ced832ff72616b8211c77978521" +source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#5c0892b6567dab31394d701477183ce9d6a32aca" dependencies = [ "zstd-safe 7.0.0", ] @@ -6200,7 +6200,7 @@ dependencies = [ [[package]] name = "zstd-safe" version = "7.0.0" -source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#7e243272c5483ced832ff72616b8211c77978521" +source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#5c0892b6567dab31394d701477183ce9d6a32aca" dependencies = [ "zstd-sys 2.0.9+zstd.1.5.5", ] @@ -6208,7 +6208,7 @@ dependencies = [ [[package]] name = "zstd-sys" version = "2.0.9+zstd.1.5.5" -source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#7e243272c5483ced832ff72616b8211c77978521" +source = "git+https://github.com/scroll-tech/zstd-rs?branch=hack/mul-block#5c0892b6567dab31394d701477183ce9d6a32aca" dependencies = [ "cc", "pkg-config", diff --git a/aggregator/src/aggregation/decoder/witgen/params.rs b/aggregator/src/aggregation/decoder/witgen/params.rs index 6b7e1ba7bd..4d7b463bd2 100644 --- a/aggregator/src/aggregation/decoder/witgen/params.rs +++ b/aggregator/src/aggregation/decoder/witgen/params.rs @@ -36,6 +36,10 @@ pub fn init_zstd_encoder( zstd::zstd_safe::ParamSwitch::Disable, )) .expect("infallible"); + // with a hack in zstd we can set window log <= 17 with single segment kept + encoder + .set_parameter(zstd::stream::raw::CParameter::WindowLog(17)) + .expect("infallible"); // set target block size to fit within a single block. encoder .set_parameter(zstd::stream::raw::CParameter::TargetCBlockSize( From f6e205f78c15be86873a942d0c9ec9c60d6f5ada Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Wed, 22 May 2024 18:27:20 +0100 Subject: [PATCH 14/15] fix: is_init continue if nil=1 --- aggregator/src/aggregation/decoder.rs | 3 +-- aggregator/src/aggregation/decoder/witgen.rs | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/aggregator/src/aggregation/decoder.rs b/aggregator/src/aggregation/decoder.rs index 9020f42865..d54d9280ba 100644 --- a/aggregator/src/aggregation/decoder.rs +++ b/aggregator/src/aggregation/decoder.rs @@ -4981,8 +4981,7 @@ mod tests { ); assert_eq!( - std::str::from_utf8(&recovered_bytes), - std::str::from_utf8(&self.raw), + recovered_bytes, self.raw, "witgen recovered bytes do not match original raw bytes", ); diff --git a/aggregator/src/aggregation/decoder/witgen.rs b/aggregator/src/aggregation/decoder/witgen.rs index bef8f2dd43..7c670e9ec3 100644 --- a/aggregator/src/aggregation/decoder/witgen.rs +++ b/aggregator/src/aggregation/decoder/witgen.rs @@ -1355,7 +1355,10 @@ fn process_sequences( let wrap_by = match to_bit_idx { 15 => 8, 16..=23 => 16, - v => unreachable!("unexpected bitstring-length={:?}", v), + v => unreachable!( + "unexpected bit_index_end={:?} in (table={:?}, update_f?={:?}) (bit_index_start={:?}, bitstring_len={:?})", + v, table_kind, (current_decoding_state >= 3), from_bit_idx, to_bit_idx - from_bit_idx + 1, + ), }; witness_rows.push(ZstdWitnessRow { state: ZstdState { @@ -1391,7 +1394,7 @@ fn process_sequences( bit_end_idx: to_bit_idx - wrap_by, bit_value: 0, is_zero_bit_read: false, - is_seq_init: false, + is_seq_init: is_init, seq_idx, states: last_states, symbols: last_symbols, From 6de0b41c583f6441a2bcd1ef36cbf9a3035a3c3e Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Wed, 22 May 2024 23:53:46 +0100 Subject: [PATCH 15/15] refactor bitstring table to separate out bitstrings of different len --- aggregator/data/test_blobs/multi/202711.hex | Bin 0 -> 110563 bytes aggregator/data/test_blobs/multi/202712.hex | Bin 0 -> 104094 bytes aggregator/src/aggregation/decoder.rs | 363 +++++++++-- .../aggregation/decoder/tables/bitstring.rs | 566 +++++++++--------- 4 files changed, 585 insertions(+), 344 deletions(-) create mode 100644 aggregator/data/test_blobs/multi/202711.hex create mode 100644 aggregator/data/test_blobs/multi/202712.hex diff --git a/aggregator/data/test_blobs/multi/202711.hex b/aggregator/data/test_blobs/multi/202711.hex new file mode 100644 index 0000000000000000000000000000000000000000..4e294edf4394c0e6f136d3c6cca291d50d131ebf GIT binary patch literal 110563 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%0AuxYGLb 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 literal 0 HcmV?d00001 diff --git a/aggregator/src/aggregation/decoder.rs b/aggregator/src/aggregation/decoder.rs index d54d9280ba..9be2933f93 100644 --- a/aggregator/src/aggregation/decoder.rs +++ b/aggregator/src/aggregation/decoder.rs @@ -87,8 +87,12 @@ pub struct DecoderConfig { pow2_table: Pow2Table<20>, /// Helper table for decoding the regenerated size from LiteralsHeader. literals_header_table: LiteralsHeaderTable, - // /// Helper table for decoding bitstreams. - bitstring_table: BitstringTable, + /// Helper table for decoding bitstreams that span over 1 byte. + bitstring_table_1: BitstringTable<1>, + /// Helper table for decoding bitstreams that span over 2 bytes. + bitstring_table_2: BitstringTable<2>, + /// Helper table for decoding bitstreams that span over 3 bytes. + bitstring_table_3: BitstringTable<3>, /// Helper table for decoding FSE tables. fse_table: FseTable, /// Helper table for sequences as instructions. @@ -595,6 +599,14 @@ impl BitstreamDecoder { not::expr(spans_one_byte) * lt2 } + /// A bistring spans 2 bytes if the 8 <= bit_index_end <= 15. + fn spans_two_bytes(&self, meta: &mut VirtualCells, at: Rotation) -> Expression { + let spans_one_byte = self.spans_one_byte(meta, at); + let lhs = meta.query_advice(self.bit_index_end, at); + let (lt2, eq2) = self.bit_index_end_cmp_15.expr_at(meta, at, lhs, 15.expr()); + not::expr(spans_one_byte) * (lt2 + eq2) + } + /// A bitstring spans 2 bytes and is byte-aligned: /// - bit_index_end == 15. fn aligned_two_bytes(&self, meta: &mut VirtualCells, at: Rotation) -> Expression { @@ -996,7 +1008,9 @@ impl DecoderConfig { ); // Helper tables let literals_header_table = LiteralsHeaderTable::configure(meta, q_enable, range8, range16); - let bitstring_table = BitstringTable::configure(meta, q_enable, range_block_len); + let bitstring_table_1 = BitstringTable::configure(meta, q_enable, range_block_len); + let bitstring_table_2 = BitstringTable::configure(meta, q_enable, range_block_len); + let bitstring_table_3 = BitstringTable::configure(meta, q_enable, range_block_len); let fse_table = FseTable::configure( meta, q_enable, @@ -1071,7 +1085,9 @@ impl DecoderConfig { range_block_len, pow2_table, literals_header_table, - bitstring_table, + bitstring_table_1, + bitstring_table_2, + bitstring_table_3, fse_table, sequence_instruction_table, @@ -4034,23 +4050,123 @@ impl DecoderConfig { }); meta.lookup_any( - "DecoderConfig: Bitstream Decoder (bitstring start)", + "DecoderConfig: Bitstream Decoder (bitstring start: bit_index_end <= 7)", + |meta| { + let condition = and::expr([ + not::expr(config.bitstream_decoder.is_nil(meta, Rotation::cur())), + not::expr(config.bitstream_decoder.is_nb0(meta, Rotation::cur())), + config + .bitstream_decoder + .spans_one_byte(meta, Rotation::cur()), + sum::expr([ + meta.query_advice(config.tag_config.is_fse_code, Rotation::cur()), + meta.query_advice(config.tag_config.is_sequence_data, Rotation::cur()), + ]), + ]); + + let byte_idx = meta.query_advice(config.byte_idx, Rotation(0)); + let byte = meta.query_advice(config.byte, Rotation(0)); + + let (bit_index_start, _bit_index_end, bitstring_value) = ( + meta.query_advice(config.bitstream_decoder.bit_index_start, Rotation::cur()), + meta.query_advice(config.bitstream_decoder.bit_index_end, Rotation::cur()), + meta.query_advice(config.bitstream_decoder.bitstring_value, Rotation::cur()), + ); + let is_reverse = meta.query_advice(config.tag_config.is_reverse, Rotation::cur()); + + [ + byte_idx, + 0.expr(), // only 1 byte + 0.expr(), // only 1 byte + byte, + 0.expr(), // only 1 byte + 0.expr(), // only 1 byte + bitstring_value, + 1.expr(), // bitstring_len at start + bit_index_start, + 1.expr(), // from_start + 1.expr(), // until_end + is_reverse, + 0.expr(), // is_padding + ] + .into_iter() + .zip_eq(config.bitstring_table_1.table_exprs(meta)) + .map(|(arg, table)| (condition.expr() * arg, table)) + .collect() + }, + ); + meta.lookup_any( + "DecoderConfig: Bitstream Decoder (bitstring start: bit_index_end <= 15)", + |meta| { + let condition = and::expr([ + not::expr(config.bitstream_decoder.is_nil(meta, Rotation::cur())), + not::expr(config.bitstream_decoder.is_nb0(meta, Rotation::cur())), + config + .bitstream_decoder + .spans_two_bytes(meta, Rotation::cur()), + sum::expr([ + meta.query_advice(config.tag_config.is_fse_code, Rotation::cur()), + meta.query_advice(config.tag_config.is_sequence_data, Rotation::cur()), + ]), + ]); + + let (byte_idx_1, byte_idx_2) = ( + meta.query_advice(config.byte_idx, Rotation(0)), + meta.query_advice(config.byte_idx, Rotation(1)), + ); + let (byte_1, byte_2) = ( + meta.query_advice(config.byte, Rotation(0)), + meta.query_advice(config.byte, Rotation(1)), + ); + let (bit_index_start, _bit_index_end, bitstring_value) = ( + meta.query_advice(config.bitstream_decoder.bit_index_start, Rotation::cur()), + meta.query_advice(config.bitstream_decoder.bit_index_end, Rotation::cur()), + meta.query_advice(config.bitstream_decoder.bitstring_value, Rotation::cur()), + ); + let is_reverse = meta.query_advice(config.tag_config.is_reverse, Rotation::cur()); + + [ + byte_idx_1, + byte_idx_2, + 0.expr(), // only 2 bytes + byte_1, + byte_2, + 0.expr(), // only 2 bytes + bitstring_value, + 1.expr(), // bitstring_len at start + bit_index_start, + 1.expr(), // from_start + 1.expr(), // until_end + is_reverse, + 0.expr(), // is_padding + ] + .into_iter() + .zip_eq(config.bitstring_table_2.table_exprs(meta)) + .map(|(arg, table)| (condition.expr() * arg, table)) + .collect() + }, + ); + meta.lookup_any( + "DecoderConfig: Bitstream Decoder (bitstring start: bit_index_end <= 23)", |meta| { let condition = and::expr([ not::expr(config.bitstream_decoder.is_nil(meta, Rotation::cur())), not::expr(config.bitstream_decoder.is_nb0(meta, Rotation::cur())), + config + .bitstream_decoder + .spans_three_bytes(meta, Rotation::cur()), sum::expr([ meta.query_advice(config.tag_config.is_fse_code, Rotation::cur()), meta.query_advice(config.tag_config.is_sequence_data, Rotation::cur()), ]), ]); - let (byte_idx0, byte_idx1, byte_idx2) = ( + let (byte_idx_1, byte_idx_2, byte_idx_3) = ( meta.query_advice(config.byte_idx, Rotation(0)), meta.query_advice(config.byte_idx, Rotation(1)), meta.query_advice(config.byte_idx, Rotation(2)), ); - let (byte0, byte1, byte2) = ( + let (byte_1, byte_2, byte_3) = ( meta.query_advice(config.byte, Rotation(0)), meta.query_advice(config.byte, Rotation(1)), meta.query_advice(config.byte, Rotation(2)), @@ -4063,12 +4179,12 @@ impl DecoderConfig { let is_reverse = meta.query_advice(config.tag_config.is_reverse, Rotation::cur()); [ - byte_idx0, - byte_idx1, - byte_idx2, - byte0, - byte1, - byte2, + byte_idx_1, + byte_idx_2, + byte_idx_3, + byte_1, + byte_2, + byte_3, bitstring_value, 1.expr(), // bitstring_len at start bit_index_start, @@ -4078,59 +4194,164 @@ impl DecoderConfig { 0.expr(), // is_padding ] .into_iter() - .zip_eq(config.bitstring_table.table_exprs(meta)) + .zip_eq(config.bitstring_table_3.table_exprs(meta)) .map(|(arg, table)| (condition.expr() * arg, table)) .collect() }, ); - meta.lookup_any("DecoderConfig: Bitstream Decoder (bitstring end)", |meta| { - let condition = and::expr([ - not::expr(config.bitstream_decoder.is_nil(meta, Rotation::cur())), - not::expr(config.bitstream_decoder.is_nb0(meta, Rotation::cur())), - sum::expr([ - meta.query_advice(config.tag_config.is_fse_code, Rotation::cur()), - meta.query_advice(config.tag_config.is_sequence_data, Rotation::cur()), - ]), - ]); + meta.lookup_any( + "DecoderConfig: Bitstream Decoder (bitstring end: bit_index_end <= 7)", + |meta| { + let condition = and::expr([ + not::expr(config.bitstream_decoder.is_nil(meta, Rotation::cur())), + not::expr(config.bitstream_decoder.is_nb0(meta, Rotation::cur())), + config + .bitstream_decoder + .spans_one_byte(meta, Rotation::cur()), + sum::expr([ + meta.query_advice(config.tag_config.is_fse_code, Rotation::cur()), + meta.query_advice(config.tag_config.is_sequence_data, Rotation::cur()), + ]), + ]); - let (byte_idx0, byte_idx1, byte_idx2) = ( - meta.query_advice(config.byte_idx, Rotation(0)), - meta.query_advice(config.byte_idx, Rotation(1)), - meta.query_advice(config.byte_idx, Rotation(2)), - ); - let (byte0, byte1, byte2) = ( - meta.query_advice(config.byte, Rotation(0)), - meta.query_advice(config.byte, Rotation(1)), - meta.query_advice(config.byte, Rotation(2)), - ); - let (bit_index_start, bit_index_end, bitstring_value) = ( - meta.query_advice(config.bitstream_decoder.bit_index_start, Rotation::cur()), - meta.query_advice(config.bitstream_decoder.bit_index_end, Rotation::cur()), - meta.query_advice(config.bitstream_decoder.bitstring_value, Rotation::cur()), - ); - let is_reverse = meta.query_advice(config.tag_config.is_reverse, Rotation::cur()); + let byte_idx = meta.query_advice(config.byte_idx, Rotation(0)); + let byte = meta.query_advice(config.byte, Rotation(0)); - [ - byte_idx0, - byte_idx1, - byte_idx2, - byte0, - byte1, - byte2, - bitstring_value, - bit_index_end.expr() - bit_index_start + 1.expr(), // bitstring_len at end - bit_index_end, - 1.expr(), // from_start - 1.expr(), // until_end - is_reverse, - 0.expr(), // is_padding - ] - .into_iter() - .zip_eq(config.bitstring_table.table_exprs(meta)) - .map(|(arg, table)| (condition.expr() * arg, table)) - .collect() - }); + let (bit_index_start, bit_index_end, bitstring_value) = ( + meta.query_advice(config.bitstream_decoder.bit_index_start, Rotation::cur()), + meta.query_advice(config.bitstream_decoder.bit_index_end, Rotation::cur()), + meta.query_advice(config.bitstream_decoder.bitstring_value, Rotation::cur()), + ); + let is_reverse = meta.query_advice(config.tag_config.is_reverse, Rotation::cur()); + + [ + byte_idx, + 0.expr(), // only 1 byte + 0.expr(), // only 1 byte + byte, + 0.expr(), // only 1 byte + 0.expr(), // only 1 byte + bitstring_value, + bit_index_end.expr() - bit_index_start + 1.expr(), // bitstring_len at end + bit_index_end, + 1.expr(), // from_start + 1.expr(), // until_end + is_reverse, + 0.expr(), // is_padding + ] + .into_iter() + .zip_eq(config.bitstring_table_1.table_exprs(meta)) + .map(|(arg, table)| (condition.expr() * arg, table)) + .collect() + }, + ); + meta.lookup_any( + "DecoderConfig: Bitstream Decoder (bitstring end: bit_index_end <= 15)", + |meta| { + let condition = and::expr([ + not::expr(config.bitstream_decoder.is_nil(meta, Rotation::cur())), + not::expr(config.bitstream_decoder.is_nb0(meta, Rotation::cur())), + config + .bitstream_decoder + .spans_two_bytes(meta, Rotation::cur()), + sum::expr([ + meta.query_advice(config.tag_config.is_fse_code, Rotation::cur()), + meta.query_advice(config.tag_config.is_sequence_data, Rotation::cur()), + ]), + ]); + + let (byte_idx_1, byte_idx_2) = ( + meta.query_advice(config.byte_idx, Rotation(0)), + meta.query_advice(config.byte_idx, Rotation(1)), + ); + let (byte_1, byte_2) = ( + meta.query_advice(config.byte, Rotation(0)), + meta.query_advice(config.byte, Rotation(1)), + ); + + let (bit_index_start, bit_index_end, bitstring_value) = ( + meta.query_advice(config.bitstream_decoder.bit_index_start, Rotation::cur()), + meta.query_advice(config.bitstream_decoder.bit_index_end, Rotation::cur()), + meta.query_advice(config.bitstream_decoder.bitstring_value, Rotation::cur()), + ); + let is_reverse = meta.query_advice(config.tag_config.is_reverse, Rotation::cur()); + + [ + byte_idx_1, + byte_idx_2, + 0.expr(), // only 2 bytes + byte_1, + byte_2, + 0.expr(), // only 2 bytes + bitstring_value, + bit_index_end.expr() - bit_index_start + 1.expr(), // bitstring_len at end + bit_index_end, + 1.expr(), // from_start + 1.expr(), // until_end + is_reverse, + 0.expr(), // is_padding + ] + .into_iter() + .zip_eq(config.bitstring_table_2.table_exprs(meta)) + .map(|(arg, table)| (condition.expr() * arg, table)) + .collect() + }, + ); + meta.lookup_any( + "DecoderConfig: Bitstream Decoder (bitstring end: bit_index_end <= 23)", + |meta| { + let condition = and::expr([ + not::expr(config.bitstream_decoder.is_nil(meta, Rotation::cur())), + not::expr(config.bitstream_decoder.is_nb0(meta, Rotation::cur())), + config + .bitstream_decoder + .spans_three_bytes(meta, Rotation::cur()), + sum::expr([ + meta.query_advice(config.tag_config.is_fse_code, Rotation::cur()), + meta.query_advice(config.tag_config.is_sequence_data, Rotation::cur()), + ]), + ]); + + let (byte_idx_1, byte_idx_2, byte_idx_3) = ( + meta.query_advice(config.byte_idx, Rotation(0)), + meta.query_advice(config.byte_idx, Rotation(1)), + meta.query_advice(config.byte_idx, Rotation(2)), + ); + let (byte_1, byte_2, byte_3) = ( + meta.query_advice(config.byte, Rotation(0)), + meta.query_advice(config.byte, Rotation(1)), + meta.query_advice(config.byte, Rotation(2)), + ); + + let (bit_index_start, bit_index_end, bitstring_value) = ( + meta.query_advice(config.bitstream_decoder.bit_index_start, Rotation::cur()), + meta.query_advice(config.bitstream_decoder.bit_index_end, Rotation::cur()), + meta.query_advice(config.bitstream_decoder.bitstring_value, Rotation::cur()), + ); + let is_reverse = meta.query_advice(config.tag_config.is_reverse, Rotation::cur()); + + [ + byte_idx_1, + byte_idx_2, + byte_idx_3, + byte_1, + byte_2, + byte_3, + bitstring_value, + bit_index_end.expr() - bit_index_start + 1.expr(), // bitstring_len at end + bit_index_end, + 1.expr(), // from_start + 1.expr(), // until_end + is_reverse, + 0.expr(), // is_padding + ] + .into_iter() + .zip_eq(config.bitstring_table_3.table_exprs(meta)) + .map(|(arg, table)| (condition.expr() * arg, table)) + .collect() + }, + ); debug_assert!(meta.degree() <= 9); @@ -4170,8 +4391,27 @@ impl DecoderConfig { //////// Assign FSE and Bitstream Accumulation ///////// ///////////////////////////////////////////////////////// self.fse_table.assign(layouter, fse_aux_tables, k)?; - self.bitstring_table - .assign(layouter, &block_info_arr, &witness_rows, k)?; + self.bitstring_table_1.assign( + layouter, + &block_info_arr, + &witness_rows, + k, + self.unusable_rows(), + )?; + self.bitstring_table_2.assign( + layouter, + &block_info_arr, + &witness_rows, + k, + self.unusable_rows(), + )?; + self.bitstring_table_3.assign( + layouter, + &block_info_arr, + &witness_rows, + k, + self.unusable_rows(), + )?; ///////////////////////////////////////// ///// Assign LiteralHeaderTable //////// @@ -5199,6 +5439,7 @@ mod tests { } #[test] + #[ignore = "multi_blob: heavy"] fn test_decoder_config_large_multi_block() -> Result<(), std::io::Error> { let mut batch_files = fs::read_dir("./data/test_blobs/multi")? .map(|entry| entry.map(|e| e.path())) diff --git a/aggregator/src/aggregation/decoder/tables/bitstring.rs b/aggregator/src/aggregation/decoder/tables/bitstring.rs index 757c69c1c3..5e5fc13ebc 100644 --- a/aggregator/src/aggregation/decoder/tables/bitstring.rs +++ b/aggregator/src/aggregation/decoder/tables/bitstring.rs @@ -16,7 +16,7 @@ use crate::{ witgen::{ZstdTag, ZstdWitnessRow}, BlockInfo, }, - witgen::N_BLOCK_SIZE_TARGET, + witgen::{N_BITS_PER_BYTE, N_BLOCK_SIZE_TARGET}, }; /// In the process of decoding zstd encoded data, there are several scenarios in which we process @@ -65,7 +65,7 @@ use crate::{ /// the bits of interest where "from_start == until_end == 1". Over these rows, we accumulate the /// binary value and the bitstring's length. #[derive(Clone, Debug)] -pub struct BitstringTable { +pub struct BitstringTable { /// Fixed column that is enabled only for the first row. pub q_first: Column, /// The byte offset of byte_1. @@ -109,13 +109,21 @@ pub struct BitstringTable { pub is_padding: Column, } -impl BitstringTable { +impl BitstringTable { /// Construct the bitstring accumulation table. pub fn configure( meta: &mut ConstraintSystem, q_enable: Column, range_block_len: RangeTable<{ N_BLOCK_SIZE_TARGET as usize }>, ) -> Self { + assert!( + (1..=3).contains(&N_BYTES), + "unexpected byte-group={:?} expected=range({:?}..={:?})", + N_BYTES, + 1, + 3, + ); + let config = Self { q_first: meta.fixed_column(), byte_idx_1: meta.advice_column(), @@ -145,8 +153,8 @@ impl BitstringTable { let mut cb = BaseConstraintBuilder::default(); - let bits = (0..24) - .map(|i| meta.query_advice(config.bit, Rotation(i))) + let bits = (0..(3 * N_BITS_PER_BYTE)) + .map(|i| meta.query_advice(config.bit, Rotation(i as i32))) .collect::>>(); let (byte_1, byte_2, byte_3) = ( @@ -179,53 +187,57 @@ impl BitstringTable { ), ); - cb.require_equal( - "byte2 is the binary accumulation of 8 <= bit_index <= 15", - byte_2, - select::expr( - meta.query_advice(config.is_reverse, Rotation::cur()), - bits[15].expr() - + bits[14].expr() * 2.expr() - + bits[13].expr() * 4.expr() - + bits[12].expr() * 8.expr() - + bits[11].expr() * 16.expr() - + bits[10].expr() * 32.expr() - + bits[9].expr() * 64.expr() - + bits[8].expr() * 128.expr(), - bits[8].expr() - + bits[9].expr() * 2.expr() - + bits[10].expr() * 4.expr() - + bits[11].expr() * 8.expr() - + bits[12].expr() * 16.expr() - + bits[13].expr() * 32.expr() - + bits[14].expr() * 64.expr() - + bits[15].expr() * 128.expr(), - ), - ); + if N_BYTES > 1 { + cb.require_equal( + "byte2 is the binary accumulation of 8 <= bit_index <= 15", + byte_2, + select::expr( + meta.query_advice(config.is_reverse, Rotation::cur()), + bits[15].expr() + + bits[14].expr() * 2.expr() + + bits[13].expr() * 4.expr() + + bits[12].expr() * 8.expr() + + bits[11].expr() * 16.expr() + + bits[10].expr() * 32.expr() + + bits[9].expr() * 64.expr() + + bits[8].expr() * 128.expr(), + bits[8].expr() + + bits[9].expr() * 2.expr() + + bits[10].expr() * 4.expr() + + bits[11].expr() * 8.expr() + + bits[12].expr() * 16.expr() + + bits[13].expr() * 32.expr() + + bits[14].expr() * 64.expr() + + bits[15].expr() * 128.expr(), + ), + ); + } - cb.require_equal( - "byte3 is the binary accumulation of 16 <= bit_index <= 23", - byte_3, - select::expr( - meta.query_advice(config.is_reverse, Rotation::cur()), - bits[23].expr() - + bits[22].expr() * 2.expr() - + bits[21].expr() * 4.expr() - + bits[20].expr() * 8.expr() - + bits[19].expr() * 16.expr() - + bits[18].expr() * 32.expr() - + bits[17].expr() * 64.expr() - + bits[16].expr() * 128.expr(), - bits[16].expr() - + bits[17].expr() * 2.expr() - + bits[18].expr() * 4.expr() - + bits[19].expr() * 8.expr() - + bits[20].expr() * 16.expr() - + bits[21].expr() * 32.expr() - + bits[22].expr() * 64.expr() - + bits[23].expr() * 128.expr(), - ), - ); + if N_BYTES > 2 { + cb.require_equal( + "byte3 is the binary accumulation of 16 <= bit_index <= 23", + byte_3, + select::expr( + meta.query_advice(config.is_reverse, Rotation::cur()), + bits[23].expr() + + bits[22].expr() * 2.expr() + + bits[21].expr() * 4.expr() + + bits[20].expr() * 8.expr() + + bits[19].expr() * 16.expr() + + bits[18].expr() * 32.expr() + + bits[17].expr() * 64.expr() + + bits[16].expr() * 128.expr(), + bits[16].expr() + + bits[17].expr() * 2.expr() + + bits[18].expr() * 4.expr() + + bits[19].expr() * 8.expr() + + bits[20].expr() * 16.expr() + + bits[21].expr() * 32.expr() + + bits[22].expr() * 64.expr() + + bits[23].expr() * 128.expr(), + ), + ); + } cb.require_boolean( "is_reverse is boolean", @@ -233,13 +245,11 @@ impl BitstringTable { ); // from_start initialises at 1 - /* cb.require_equal( "if bit_index == 0: from_start == 1", meta.query_advice(config.from_start, Rotation::cur()), 1.expr(), ); - */ cb.gate(condition) }); @@ -311,7 +321,6 @@ impl BitstringTable { let delta = meta.query_advice(config.until_end, Rotation::next()) - meta.query_advice(config.until_end, Rotation::cur()); - /* cb.condition(is_end.expr(), |cb| { cb.require_equal( "if bit_index == 23: until_end == 1", @@ -319,7 +328,6 @@ impl BitstringTable { 1.expr(), ); }); - */ cb.condition(not::expr(is_end.expr()), |cb| { cb.require_boolean("until_end delta is boolean", delta); }); @@ -465,249 +473,241 @@ impl BitstringTable { block_info_arr: &Vec, witness_rows: &[ZstdWitnessRow], k: u32, + unusable_rows: usize, ) -> Result<(), Error> { - assert!(!witness_rows.is_empty()); - layouter.assign_region( - || "Bitstring Accumulation Table", + || "Bitstring Table", |mut region| { let mut offset: usize = 0; region.assign_fixed(|| "q_first", self.q_first, 0, || Value::known(Fr::one()))?; - for i in 0..(((1 << k) - 30) / 24) { - for bit_idx in 0..24 { + // assign fixed columns. + let n_enabled = (1 << k) - unusable_rows; + for i in 0..n_enabled { + let bit_index = i % (N_BYTES * N_BITS_PER_BYTE); + if bit_index == 0 { region.assign_fixed( - || "bit_index", - self.bit_index, - 24 * i + bit_idx, - || Value::known(Fr::from(bit_idx as u64)), + || "q_start", + self.q_start, + i, + || Value::known(Fr::one()), )?; } region.assign_fixed( - || "q_start", - self.q_start, - 24 * i, - || Value::known(Fr::one()), + || "bit_index", + self.bit_index, + i, + || Value::known(Fr::from(bit_index as u64)), )?; } - // Multi-block assignment - for block in block_info_arr { - // Fse decoding rows - let fse_position = witness_rows - .iter() - .position(|r| { - r.state.block_idx == (block.block_idx as u64) - && r.state.tag == ZstdTag::ZstdBlockSequenceFseCode - }) - .unwrap(); - let mut fse_rows = witness_rows - .iter() - .filter(|&r| { - r.state.block_idx == (block.block_idx as u64) - && r.state.tag == ZstdTag::ZstdBlockSequenceFseCode - }) - .map(|r| { - ( - r.encoded_data.byte_idx as usize, - r.encoded_data.value_byte as u64, - r.bitstream_read_data.bit_start_idx, - r.bitstream_read_data.bit_end_idx, - r.bitstream_read_data.bit_value, - r.state.tag.is_reverse() as u64, - ) - }) - .collect::>(); - // Append 2 more witness rows to accommodate the 3-bytes chunk for the last - // FseCode row. - let fse_rows_len = fse_rows.len(); - fse_rows.extend_from_slice( - witness_rows - .iter() - .skip(fse_position + fse_rows_len) - .take(2) - .map(|r| { - ( - r.encoded_data.byte_idx as usize, - r.encoded_data.value_byte as u64, - r.bitstream_read_data.bit_start_idx, - r.bitstream_read_data.bit_end_idx, - r.bitstream_read_data.bit_value, - r.state.tag.is_reverse() as u64, - ) - }) - .collect::>() - .as_slice(), - ); - - // Sequence data rows - let sequence_data_position = witness_rows - .iter() - .position(|r| { - r.state.block_idx == (block.block_idx as u64) - && r.state.tag == ZstdTag::ZstdBlockSequenceData - }) - .unwrap(); - let mut sequence_data_rows = witness_rows - .iter() - .filter(|&r| { - r.state.block_idx == (block.block_idx as u64) - && r.state.tag == ZstdTag::ZstdBlockSequenceData - }) - .map(|r| { - ( - r.encoded_data.byte_idx as usize, - r.encoded_data.value_byte as u64, - r.bitstream_read_data.bit_start_idx, - r.bitstream_read_data.bit_end_idx, - r.bitstream_read_data.bit_value, - r.state.tag.is_reverse() as u64, - ) - }) - .collect::>(); - // Append 2 more witness rows to accommodate the 3-bytes chunk for the last - // FseCode row. - let sequence_data_rows_len = sequence_data_rows.len(); - let padding_byte_idx = sequence_data_rows.last().unwrap().0 + 1; - let extension = witness_rows - .iter() - .skip(sequence_data_position + sequence_data_rows_len) - .take(2) - .map(|r| { - ( - r.encoded_data.byte_idx as usize, - r.encoded_data.value_byte as u64, - r.bitstream_read_data.bit_start_idx, - r.bitstream_read_data.bit_end_idx, - r.bitstream_read_data.bit_value, - r.state.tag.is_reverse() as u64, - ) - }) - .chain(std::iter::repeat((padding_byte_idx, 0, 0, 0, 0, 0))) - .take(2) - .collect::>(); - sequence_data_rows.extend_from_slice(&extension); - - for rows in [fse_rows, sequence_data_rows].into_iter() { - for grouped_rows in rows.windows(3) { - let curr_row = grouped_rows[0]; - - let byte_idx_1 = grouped_rows[0].0; - let byte_idx_2 = grouped_rows[1].0; - let byte_idx_3 = grouped_rows[2].0; - let byte_1 = grouped_rows[0].1; - let byte_2 = grouped_rows[1].1; - let byte_3 = grouped_rows[2].1; - - let byte_1_bits = value_bits_le(byte_1 as u8); - let byte_2_bits = value_bits_le(byte_2 as u8); - let byte_3_bits = value_bits_le(byte_3 as u8); - - let bits = if curr_row.5 > 0 { - // reversed - [ - byte_1_bits.into_iter().rev().collect::>(), - byte_2_bits.into_iter().rev().collect::>(), - byte_3_bits.into_iter().rev().collect::>(), - ] - .concat() - } else { - // not reversed - [byte_1_bits, byte_2_bits, byte_3_bits].concat() - }; - - let mut acc: u64 = 0; - let mut bitstring_len: u64 = 0; - - for (bit_idx, bit) in bits.into_iter().enumerate().take(24) { - region.assign_advice( - || "byte_idx_1", - self.byte_idx_1, - offset + bit_idx, - || Value::known(Fr::from(byte_idx_1 as u64)), - )?; - region.assign_advice( - || "byte_idx_2", - self.byte_idx_2, - offset + bit_idx, - || Value::known(Fr::from(byte_idx_2 as u64)), - )?; - region.assign_advice( - || "byte_idx_3", - self.byte_idx_3, - offset + bit_idx, - || Value::known(Fr::from(byte_idx_3 as u64)), - )?; - region.assign_advice( - || "byte_1", - self.byte_1, - offset + bit_idx, - || Value::known(Fr::from(byte_1)), - )?; - region.assign_advice( - || "byte_2", - self.byte_2, - offset + bit_idx, - || Value::known(Fr::from(byte_2)), - )?; - region.assign_advice( - || "byte_3", - self.byte_3, - offset + bit_idx, - || Value::known(Fr::from(byte_3)), - )?; - - if bit_idx >= curr_row.2 && bit_idx <= curr_row.3 { - acc = acc * 2 + (bit as u64); - bitstring_len += 1; - } - region.assign_advice( - || "bit", - self.bit, - offset + bit_idx, - || Value::known(Fr::from(bit as u64)), - )?; - region.assign_advice( - || "bitstring_value", - self.bitstring_value, - offset + bit_idx, - || Value::known(Fr::from(curr_row.4)), - )?; - region.assign_advice( - || "bitstring_value_acc", - self.bitstring_value_acc, - offset + bit_idx, - || Value::known(Fr::from(acc)), - )?; - region.assign_advice( - || "bitstring_len", - self.bitstring_len, - offset + bit_idx, - || Value::known(Fr::from(bitstring_len)), - )?; - region.assign_advice( - || "from_start", - self.from_start, - offset + bit_idx, - || Value::known(Fr::from((bit_idx <= curr_row.3) as u64)), - )?; - region.assign_advice( - || "until_end", - self.until_end, - offset + bit_idx, - || Value::known(Fr::from((bit_idx >= curr_row.2) as u64)), - )?; - region.assign_advice( - || "is_reverse", - self.is_reverse, - offset + bit_idx, - || Value::known(Fr::from(curr_row.5)), - )?; + let n_witness_rows = witness_rows.len(); + let filler_row = ( + witness_rows + .last() + .expect("at least 1 row") + .encoded_data + .byte_idx + + 1, + 0u64, + 0u64, + 0u64, + 0u64, + 0u64, + ); + + let mut rows = Vec::with_capacity(n_enabled); + for block_info in block_info_arr { + for i in 0..n_witness_rows { + // prechecks. + let witness_row = &witness_rows[i]; + if witness_row.state.block_idx > (block_info.block_idx as u64) { + break; + } + if witness_row.state.block_idx != (block_info.block_idx as u64) + || witness_row.bitstream_read_data.is_nil + || witness_row.bitstream_read_data.is_zero_bit_read + { + continue; + } + + let is_tag_ok = matches!( + witness_row.state.tag, + ZstdTag::ZstdBlockSequenceFseCode | ZstdTag::ZstdBlockSequenceData + ); + let bit_end_idx = witness_row.bitstream_read_data.bit_end_idx; + let is_bitstring_ok = (bit_end_idx >= ((N_BYTES - 1) * N_BITS_PER_BYTE)) + && (bit_end_idx < N_BYTES * N_BITS_PER_BYTE); + if is_tag_ok && is_bitstring_ok { + rows.push(( + witness_row.encoded_data.byte_idx, + witness_row.encoded_data.value_byte as u64, + witness_row.bitstream_read_data.bit_start_idx as u64, + witness_row.bitstream_read_data.bit_end_idx as u64, + witness_row.bitstream_read_data.bit_value, + witness_row.state.tag.is_reverse() as u64, + )); + if N_BYTES > 1 { + let next_row = if i + 1 < n_witness_rows { + ( + witness_rows[i + 1].encoded_data.byte_idx, + witness_rows[i + 1].encoded_data.value_byte as u64, + witness_rows[i + 1].bitstream_read_data.bit_start_idx + as u64, + witness_rows[i + 1].bitstream_read_data.bit_end_idx as u64, + witness_rows[i + 1].bitstream_read_data.bit_value, + witness_rows[i + 1].state.tag.is_reverse() as u64, + ) + } else { + filler_row + }; + rows.push(next_row); + } + if N_BYTES > 2 { + let next_next_row = if i + 2 < n_witness_rows { + ( + witness_rows[i + 2].encoded_data.byte_idx, + witness_rows[i + 2].encoded_data.value_byte as u64, + witness_rows[i + 2].bitstream_read_data.bit_start_idx + as u64, + witness_rows[i + 2].bitstream_read_data.bit_end_idx as u64, + witness_rows[i + 2].bitstream_read_data.bit_value, + witness_rows[i + 2].state.tag.is_reverse() as u64, + ) + } else { + filler_row + }; + rows.push(next_next_row); } + } + } + } - offset += 24; + for grouped_rows in rows.chunks_exact(N_BYTES) { + let curr_row = grouped_rows[0]; + + let (byte_idx_1, byte_idx_2, byte_idx_3) = match N_BYTES { + 1 => (grouped_rows[0].0, 0, 0), + 2 => (grouped_rows[0].0, grouped_rows[1].0, 0), + 3 => (grouped_rows[0].0, grouped_rows[1].0, grouped_rows[2].0), + v => unreachable!("unexpected N_BYTES={:?}", v), + }; + let (byte_1, byte_2, byte_3) = match N_BYTES { + 1 => (grouped_rows[0].1, 0, 0), + 2 => (grouped_rows[0].1, grouped_rows[1].1, 0), + 3 => (grouped_rows[0].1, grouped_rows[1].1, grouped_rows[2].1), + v => unreachable!("unexpected N_BYTES={:?}", v), + }; + + let byte_1_bits = value_bits_le(byte_1 as u8); + let byte_2_bits = value_bits_le(byte_2 as u8); + let byte_3_bits = value_bits_le(byte_3 as u8); + + let bits = if curr_row.5 > 0 { + // reversed + [ + byte_1_bits.into_iter().rev().collect::>(), + byte_2_bits.into_iter().rev().collect::>(), + byte_3_bits.into_iter().rev().collect::>(), + ] + .concat() + } else { + // not reversed + [byte_1_bits, byte_2_bits, byte_3_bits].concat() + }; + + let mut acc: u64 = 0; + let mut bitstring_len: u64 = 0; + + for (bit_idx, bit) in + bits.into_iter().enumerate().take(N_BYTES * N_BITS_PER_BYTE) + { + region.assign_advice( + || "byte_idx_1", + self.byte_idx_1, + offset + bit_idx, + || Value::known(Fr::from(byte_idx_1)), + )?; + region.assign_advice( + || "byte_idx_2", + self.byte_idx_2, + offset + bit_idx, + || Value::known(Fr::from(byte_idx_2)), + )?; + region.assign_advice( + || "byte_idx_3", + self.byte_idx_3, + offset + bit_idx, + || Value::known(Fr::from(byte_idx_3)), + )?; + region.assign_advice( + || "byte_1", + self.byte_1, + offset + bit_idx, + || Value::known(Fr::from(byte_1)), + )?; + region.assign_advice( + || "byte_2", + self.byte_2, + offset + bit_idx, + || Value::known(Fr::from(byte_2)), + )?; + region.assign_advice( + || "byte_3", + self.byte_3, + offset + bit_idx, + || Value::known(Fr::from(byte_3)), + )?; + + if bit_idx >= (curr_row.2 as usize) && bit_idx <= (curr_row.3 as usize) { + acc = acc * 2 + (bit as u64); + bitstring_len += 1; } + region.assign_advice( + || "bit", + self.bit, + offset + bit_idx, + || Value::known(Fr::from(bit as u64)), + )?; + region.assign_advice( + || "bitstring_value", + self.bitstring_value, + offset + bit_idx, + || Value::known(Fr::from(curr_row.4)), + )?; + region.assign_advice( + || "bitstring_value_acc", + self.bitstring_value_acc, + offset + bit_idx, + || Value::known(Fr::from(acc)), + )?; + region.assign_advice( + || "bitstring_len", + self.bitstring_len, + offset + bit_idx, + || Value::known(Fr::from(bitstring_len)), + )?; + region.assign_advice( + || "from_start", + self.from_start, + offset + bit_idx, + || Value::known(Fr::from((bit_idx <= (curr_row.3 as usize)) as u64)), + )?; + region.assign_advice( + || "until_end", + self.until_end, + offset + bit_idx, + || Value::known(Fr::from((bit_idx >= (curr_row.2 as usize)) as u64)), + )?; + region.assign_advice( + || "is_reverse", + self.is_reverse, + offset + bit_idx, + || Value::known(Fr::from(curr_row.5)), + )?; } + + offset += N_BYTES * N_BITS_PER_BYTE; } for idx in 0..offset { @@ -718,7 +718,7 @@ impl BitstringTable { || Value::known(Fr::zero()), )?; } - for idx in offset..((1 << k) - 30) { + for idx in offset..n_enabled { region.assign_advice( || "is_padding", self.is_padding, @@ -733,7 +733,7 @@ impl BitstringTable { } } -impl LookupTable for BitstringTable { +impl LookupTable for BitstringTable { fn columns(&self) -> Vec> { vec![ self.byte_idx_1.into(),