diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3fa87ba..f43acd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,32 +15,32 @@ jobs: fmt: name: Formatting runs-on: ubuntu-latest + container: rust:alpine steps: + - run: apk add --no-cache git - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt + - run: rustup component add rustfmt - run: cargo fmt --all -- --check clippy: name: Lint (clippy) runs-on: ubuntu-latest + container: rust:alpine steps: + - run: apk add --no-cache musl-dev perl make git - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - components: clippy + - run: rustup component add clippy - uses: Swatinem/rust-cache@v2 - run: cargo clippy --workspace --all-targets compile-wasm: name: Compile WASM target runs-on: ubuntu-latest + container: rust:alpine steps: + - run: apk add --no-cache git - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - targets: wasm32-unknown-unknown + - run: rustup target add wasm32-unknown-unknown - uses: Swatinem/rust-cache@v2 - name: Check crypto crate compiles for WASM run: cargo check -p salvium-crypto --target wasm32-unknown-unknown @@ -50,9 +50,10 @@ jobs: name: Compile workspace needs: [fmt, clippy, compile-wasm] runs-on: ubuntu-latest + container: rust:alpine steps: + - run: apk add --no-cache musl-dev perl make git - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: Build all crates run: cargo build --workspace @@ -64,9 +65,10 @@ jobs: name: "Test: types + consensus rules" needs: [compile] runs-on: ubuntu-latest + container: rust:alpine steps: + - run: apk add --no-cache musl-dev perl make git - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: salvium-types run: cargo test -p salvium-types -- --nocapture @@ -77,9 +79,10 @@ jobs: name: "Test: crypto verification" needs: [compile] runs-on: ubuntu-latest + container: rust:alpine steps: + - run: apk add --no-cache musl-dev perl make git - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: salvium-crypto (unit) run: cargo test -p salvium-crypto --lib -- --nocapture @@ -90,9 +93,10 @@ jobs: name: "Test: wallet + transactions + CLI" needs: [compile] runs-on: ubuntu-latest + container: rust:alpine steps: + - run: apk add --no-cache musl-dev perl make git - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: salvium-wallet (unit + integration compile) run: cargo test -p salvium-wallet -- --nocapture @@ -105,9 +109,10 @@ jobs: name: "Test: miner + multisig + RPC" needs: [compile] runs-on: ubuntu-latest + container: rust:alpine steps: + - run: apk add --no-cache musl-dev perl make git - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: salvium-miner run: cargo test -p salvium-miner -- --nocapture @@ -120,9 +125,10 @@ jobs: name: Documentation needs: [compile] runs-on: ubuntu-latest + container: rust:alpine steps: + - run: apk add --no-cache musl-dev perl make git - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: Doc tests run: cargo test --workspace --doc diff --git a/Cargo.lock b/Cargo.lock index 1672f3b..bf062a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -354,22 +354,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - [[package]] name = "cpufeatures" version = "0.2.17" @@ -548,15 +532,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - [[package]] name = "env_filter" version = "1.0.0" @@ -580,12 +555,6 @@ dependencies = [ "log", ] -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - [[package]] name = "errno" version = "0.3.14" @@ -636,27 +605,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -774,25 +722,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -802,19 +731,13 @@ dependencies = [ "ahash", ] -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - [[package]] name = "hashlink" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "hashbrown 0.14.5", + "hashbrown", ] [[package]] @@ -924,7 +847,6 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", @@ -953,22 +875,6 @@ dependencies = [ "webpki-roots", ] -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - [[package]] name = "hyper-util" version = "0.1.20" @@ -987,11 +893,9 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", - "system-configuration", "tokio", "tower-service", "tracing", - "windows-registry", ] [[package]] @@ -1108,16 +1012,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "indexmap" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", -] - [[package]] name = "inout" version = "0.1.4" @@ -1290,12 +1184,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "minicov" version = "0.3.8" @@ -1339,23 +1227,6 @@ dependencies = [ "pxfm", ] -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1445,38 +1316,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "openssl" -version = "0.10.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - [[package]] name = "openssl-src" version = "300.5.5+3.5.5" @@ -1873,22 +1712,17 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64", "bytes", - "encoding_rs", "futures-channel", "futures-core", "futures-util", - "h2", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", - "hyper-tls", "hyper-util", "js-sys", "log", - "mime", - "native-tls", "percent-encoding", "pin-project-lite", "quinn", @@ -1899,7 +1733,6 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-native-tls", "tokio-rustls", "tower", "tower-http", @@ -2278,15 +2111,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "scopeguard" version = "1.2.0" @@ -2307,29 +2131,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.11.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "1.0.27" @@ -2525,27 +2326,6 @@ dependencies = [ "syn", ] -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags 2.11.0", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tempfile" version = "3.24.0" @@ -2661,16 +2441,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.4" @@ -2681,19 +2451,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - [[package]] name = "tower" version = "0.5.3" @@ -3004,35 +2761,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.48.0" diff --git a/crates/salvium-rpc/Cargo.toml b/crates/salvium-rpc/Cargo.toml index b61ed10..3130b10 100644 --- a/crates/salvium-rpc/Cargo.toml +++ b/crates/salvium-rpc/Cargo.toml @@ -6,7 +6,7 @@ description = "Async and blocking RPC clients for Salvium daemon and wallet" [dependencies] salvium-types = { path = "../salvium-types" } -reqwest = { version = "0.12", features = ["json"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } tokio = { version = "1", features = ["rt", "time", "macros"] } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/salvium-wallet/Cargo.toml b/crates/salvium-wallet/Cargo.toml index ca46e67..0e606ff 100644 --- a/crates/salvium-wallet/Cargo.toml +++ b/crates/salvium-wallet/Cargo.toml @@ -22,7 +22,7 @@ ml-kem = { version = "0.2", features = ["deterministic"] } hkdf = "0.12" sha2 = "0.10" chacha20 = "0.9" -reqwest = { version = "0.12", features = ["json"], default-features = false, optional = true } +reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"], optional = true } hidapi = { version = "2.6", optional = true } [features] diff --git a/crates/salvium-wallet/src/sync.rs b/crates/salvium-wallet/src/sync.rs index d5e76b6..b4a05c2 100644 --- a/crates/salvium-wallet/src/sync.rs +++ b/crates/salvium-wallet/src/sync.rs @@ -52,10 +52,6 @@ struct BatchController { max_batch: usize, target_batch_time_ms: u64, consecutive_errors: u32, - /// Approximate max bytes per batch (mobile-friendly cap). - max_batch_bytes: usize, - /// Last observed bytes per second. - last_bytes_per_sec: f64, } impl BatchController { @@ -66,16 +62,11 @@ impl BatchController { max_batch: 1000, target_batch_time_ms: 1000, consecutive_errors: 0, - max_batch_bytes: 2 * 1024 * 1024, // 2 MB - last_bytes_per_sec: 0.0, } } - /// Adjust batch size based on timing and throughput. - /// - /// `blocks` is the number of blocks in the batch, `bytes` is the total - /// response size, `elapsed_ms` is how long the batch took. - fn adjust(&mut self, elapsed_ms: u64, had_error: bool, blocks: usize, bytes: usize) { + /// Adjust batch size based on elapsed time per batch. + fn adjust(&mut self, elapsed_ms: u64, had_error: bool) { if had_error { self.batch_size = (self.batch_size / 2).max(self.min_batch); self.consecutive_errors += 1; @@ -87,26 +78,11 @@ impl BatchController { self.consecutive_errors = 0; - // Track throughput. - if elapsed_ms > 0 { - self.last_bytes_per_sec = bytes as f64 / (elapsed_ms as f64 / 1000.0); - } - - // Time-based scaling. if elapsed_ms < self.target_batch_time_ms { self.batch_size = (self.batch_size + self.batch_size / 2).min(self.max_batch); } else { self.batch_size = (self.batch_size - self.batch_size / 4).max(self.min_batch); } - - // Byte-cap: if the batch exceeded the byte limit, scale down proportionally. - if blocks > 0 && bytes > self.max_batch_bytes { - let avg_block_bytes = bytes / blocks; - if avg_block_bytes > 0 { - let byte_limited = self.max_batch_bytes / avg_block_bytes; - self.batch_size = self.batch_size.min(byte_limited).max(self.min_batch); - } - } } /// Return the batch size for the next round, capped by remaining blocks. @@ -229,7 +205,7 @@ impl SyncEngine { let batch_data = match batch_result { Ok(r) => r, Err(e) => { - controller.adjust(batch_timer.elapsed().as_millis() as u64, true, 0, 0); + controller.adjust(batch_timer.elapsed().as_millis() as u64, true); if let Some(tx) = event_tx { let _ = tx.send(SyncEvent::Error(e.to_string())).await; } @@ -275,7 +251,7 @@ impl SyncEngine { } current = reorg_start; - controller.adjust(batch_timer.elapsed().as_millis() as u64, true, 0, 0); + controller.adjust(batch_timer.elapsed().as_millis() as u64, true); continue; } } @@ -283,7 +259,7 @@ impl SyncEngine { } if bin_blocks.len() != heights.len() { - controller.adjust(batch_timer.elapsed().as_millis() as u64, true, 0, 0); + controller.adjust(batch_timer.elapsed().as_millis() as u64, true); if let Some(tx) = event_tx { let _ = tx .send(SyncEvent::Error(format!( @@ -315,12 +291,6 @@ impl SyncEngine { } // ── 3. Parallel parse + sequential store ──────────────────── - // Estimate batch bytes for throughput tracking. - let batch_bytes: usize = bin_blocks - .iter() - .map(|e| e.block.len() + e.txs.iter().map(|t| t.len()).sum::()) - .sum(); - // Phase 1: Parse + scan all blocks in parallel via spawn_blocking. let scan_ctx_clone = scan_ctx.clone(); let parse_results: Vec = { @@ -521,13 +491,7 @@ impl SyncEngine { } // ── 6. Adapt batch size + race nodes ──────────────────────── - let n_blocks = bin_blocks.len(); - controller.adjust( - batch_timer.elapsed().as_millis() as u64, - false, - n_blocks, - batch_bytes, - ); + controller.adjust(batch_timer.elapsed().as_millis() as u64, false); pool.maybe_race().await; } @@ -2470,9 +2434,9 @@ mod tests { fn test_batch_controller_scale_up() { let mut ctrl = BatchController::new(); // Fast batch → scale up by 50% - ctrl.adjust(500, false, 64, 100_000); // 500ms < 1s target + ctrl.adjust(500, false); // 500ms < 1s target assert_eq!(ctrl.batch_size, 96); // 64 + 32 - ctrl.adjust(500, false, 96, 150_000); + ctrl.adjust(500, false); assert_eq!(ctrl.batch_size, 144); // 96 + 48 } @@ -2481,7 +2445,7 @@ mod tests { let mut ctrl = BatchController::new(); ctrl.batch_size = 40; // Slow batch → scale down by 25% - ctrl.adjust(5000, false, 40, 100_000); // 5s > 3s target + ctrl.adjust(5000, false); // 5s > 3s target assert_eq!(ctrl.batch_size, 30); // 40 - 10 } @@ -2489,7 +2453,7 @@ mod tests { fn test_batch_controller_error_halves() { let mut ctrl = BatchController::new(); ctrl.batch_size = 20; - ctrl.adjust(0, true, 0, 0); + ctrl.adjust(0, true); assert_eq!(ctrl.batch_size, 10); // 20 / 2 assert_eq!(ctrl.consecutive_errors, 1); } @@ -2498,9 +2462,9 @@ mod tests { fn test_batch_controller_consecutive_errors_drop_to_min() { let mut ctrl = BatchController::new(); ctrl.batch_size = 50; - ctrl.adjust(0, true, 0, 0); // 25 - ctrl.adjust(0, true, 0, 0); // 12 - ctrl.adjust(0, true, 0, 0); // 3 consecutive → drops to min + ctrl.adjust(0, true); // 25 + ctrl.adjust(0, true); // 12 + ctrl.adjust(0, true); // 3 consecutive → drops to min assert_eq!(ctrl.batch_size, ctrl.min_batch); assert_eq!(ctrl.consecutive_errors, 3); } @@ -2508,10 +2472,10 @@ mod tests { #[test] fn test_batch_controller_error_resets_on_success() { let mut ctrl = BatchController::new(); - ctrl.adjust(0, true, 0, 0); - ctrl.adjust(0, true, 0, 0); + ctrl.adjust(0, true); + ctrl.adjust(0, true); assert_eq!(ctrl.consecutive_errors, 2); - ctrl.adjust(1000, false, 64, 100_000); + ctrl.adjust(1000, false); assert_eq!(ctrl.consecutive_errors, 0); } @@ -2519,7 +2483,7 @@ mod tests { fn test_batch_controller_respects_max() { let mut ctrl = BatchController::new(); ctrl.batch_size = 90; - ctrl.adjust(100, false, 90, 100_000); // fast → scale up + ctrl.adjust(100, false); // fast → scale up assert!(ctrl.batch_size <= ctrl.max_batch); } @@ -2527,7 +2491,7 @@ mod tests { fn test_batch_controller_respects_min() { let mut ctrl = BatchController::new(); ctrl.batch_size = 2; - ctrl.adjust(10000, false, 2, 50_000); // slow → scale down + ctrl.adjust(10000, false); // slow → scale down assert!(ctrl.batch_size >= ctrl.min_batch); }