Skip to content

Conversation

@jackzhhuang
Copy link
Contributor

@jackzhhuang jackzhhuang commented Oct 20, 2025

1, add benchmark for testing vm2 transaction execution
2, cache the vm2 config from shengguo lu
3, add batch operations for the transaction interface

Pull request type

Please check the type of change your PR introduces:

  • Bugfix
  • Feature
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • Documentation content changes
  • Other (please describe):

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Other information

Summary by CodeRabbit

  • New Features

    • Dual‑VM tooling: VM2 transaction flows, dual‑account pressure tests, batch signing/unlock/submission, and a tx factory mocker for stress testing.
  • API

    • New VM2 and batch RPCs and client methods for account, txpool, and signing/unlock in batch.
  • Infrastructure

    • Cluster manifest scaled to 3 replicas; release packaging now includes tx‑factory artifacts.
  • Improvements

    • Simplified dual‑VM execution using in‑memory state and upfront block‑metadata execution to reduce contention.
  • Tests

    • New end‑to‑end integration test covering VM2 build/execute and transaction lifecycle.

2, cache the vm2 config from shengguo lu
3, add batch operations for the transaction interface
@jackzhhuang jackzhhuang requested a review from sanlee42 as a code owner October 20, 2025 08:52
@coderabbitai
Copy link

coderabbitai bot commented Oct 20, 2025

Walkthrough

Replace forked VM state handling with direct in-memory statedb use for dual‑VM execution and add broad VM2 support: CLI txn factory, RPC batch APIs, account batch services, client plumbing, txpool visibility, tests, node helpers, configs, and Kubernetes manifest updates.

Changes

Cohort / File(s) Summary
Chain / VM execution
chain/src/chain.rs
Replace forked VM state access with direct self.statedb.0/self.statedb.1, use self.epoch for gas/epoch refs, flush/commit on in‑memory statedbs, and adjust DAG finalization write/commit flows.
TX Factory CLI & Core
cmd/tx-factory/src/main.rs, cmd/tx-factory/src/lib.rs
Add vm_version and vm2 account/receiver options, new get_account_or_default2(), update TxFactoryOpt fields and wiring for vm2 paths.
TX Factory Modules
cmd/tx-factory/src/mocker.rs, cmd/tx-factory/src/txn_generator.rs, cmd/tx-factory/src/lib.rs
New TxnMocker with dual-account, batch signing/submission, stress utilities; extend MockTxnGenerator with VM2 builders/types and add exported modules.
Pressure Test Runners
cmd/tx-factory/src/pressure_vm1.rs, cmd/tx-factory/src/pressure_vm2.rs
New start_vm1_pressure_test() and start_vm2_pressure_test() threads that initialize accounts and loop submitting VM1/VM2 txns or stress tests until stopped.
RPC — TxPool batch APIs & server
rpc/api/src/txpool/mod.rs, rpc/server/src/module/txpool_rpc.rs
Add submit_transactions2() and next_sequence_number_in_batch2() API methods and server handlers; helper submit_transactions_multi() for multi‑signed imports.
RPC client & state reader
rpc/client/src/lib.rs, rpc/client/src/remote_state_reader2/mod.rs
Add v2 RPC client wrappers (next_sequence_number_in_txpool2, next_sequence_number_in_batch2, submit_transactions2, account_sign_txn_in_batch2, account_unlock_in_batch2) and get_balance2().
VM2 account API & service
vm2/account/api/src/message.rs, vm2/account/api/src/service.rs, vm2/service/account/src/service.rs
Add batch request variants (SignTxnInBatch, UnlockAccountInBatch) and response SignedTxnList; expose batch methods and route them in AccountService.
VM2 AccountManager
vm2/account/src/account_manager.rs
Implement unlock_account_in_batch() and sign_txn_in_batch() with per-item unlock/password caching and batch signing behavior.
VM2 RPC endpoints
vm2/rpc/api/src/account_api.rs, vm2/rpc/server/src/account_rpc.rs
Add RPC endpoints sign_txn_in_batch() and unlock_in_batch() delegating to account manager batch ops.
Parallel executor: block meta handling
commons/parallel-executor/src/executor.rs, commons/parallel-executor/src/task.rs
Execute block metadata txn first (new helper), add is_block_meta_data() default on Transaction trait and ensure scheduler ordering.
TxPool visibility
txpool/src/tx_pool_service_impl.rs
Promote queue, import_txns(), get_pending(), and get_pool_client() from crate‑private to public.
Node & config helpers
config/src/genesis_config/mod.rs, config/src/lib.rs, node/src/lib.rs
Make CustomNetworkID::new() public, add NodeConfig::new_for_halley(), and add NodeHandle::storage2() accessor.
Testing & helpers
test-helper/src/lib.rs, test-helper/src/node.rs, sync/tests/full_build_and_execute_test.rs
Export/add run_node_with_all_service() and a new integration test exercising VM2 account creation, batch signing, txpool import/monitoring and an ObserverService for results.
Build / manifests / scripts
rpc/server/Cargo.toml, vm2/executor/Cargo.toml, sync/Cargo.toml, kube/manifest/starcoin-halley.yaml, scripts/release.sh
Add VM2/dev dependencies and logger, update Halley manifest (replicas, image, startup script, remove readiness probe), and include starcoin_txfactory in release assets.
VM runtime & small edits
vm2/vm-runtime/src/*, vm2/framework/gas-meter/src/gas_meter.rs, vm2/aggregator/src/aggregator_v1_extension.rs
Warm VM cache now returns Result and inserts only on success; add is_block_meta_data() for preprocessed txns; add informational logs and minor formatting changes.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as TxFactory CLI
    participant Mocker as TxnMocker
    participant AccountVM2 as AccountService (VM2)
    participant RPC as RpcClient
    participant TxPool as TxPool Service
    rect #f0f9ff
    CLI->>Mocker: construct(vm1_account, vm2_account, generator)
    CLI->>Mocker: start pressure (vm_version)
    end
    alt vm_version == 1
        Mocker->>RPC: submit_transactions(Vec<SignedUserTransaction>)
        RPC->>TxPool: import_txns(MultiSignedUserTransaction::VM1)
    else vm_version == 2
        Mocker->>AccountVM2: unlock_account_in_batch(batch)
        AccountVM2-->>Mocker: Vec<AccountInfo2>
        Mocker->>AccountVM2: sign_txn_in_batch(raw_txns)
        AccountVM2-->>Mocker: Vec<SignedUserTransaction2>
        Mocker->>RPC: submit_transactions2(Vec<SignedUserTransaction2>)
        RPC->>TxPool: import_txns(MultiSignedUserTransaction::VM2)
    end
    Note over TxPool: Pool enqueues and schedules execution (block metadata executed first)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • sanlee42
  • lushengguo
  • welbon

Poem

🐇 I hopped through branches, two VMs in sight,

No forks — just memory, tidy and light,
Batches signed and pools hum through the night,
Nodes wake, tests run, configs set just right,
I twitch my whiskers — commits take flight!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.57% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title "VM2 benchmark" refers to a real aspect of the changeset—the PR does include benchmarking/pressure testing infrastructure (pressure_vm1.rs, pressure_vm2.rs, stress_test methods). However, the title is vague and overly narrow for the actual scope of changes. The PR encompasses significant infrastructure beyond benchmarking, including: extensive VM2 transaction factory improvements (TxnMocker, batch account creation), new batch operation RPC APIs (submit_transactions2, sign_txn_in_batch, unlock_in_batch), dual-VM execution flow refactoring in chain.rs, transaction pool configuration updates, and VM2 runtime enhancements. A developer scanning pull request history would see "VM2 benchmark" and might not understand that the PR adds comprehensive transaction factory infrastructure and batch operation support rather than focusing primarily on benchmarking. Consider revising the title to better reflect the primary objectives. A more descriptive title such as "Add VM2 transaction factory infrastructure with batch operations and pressure testing" or "Implement VM2 batch transaction support and dual-VM pressure testing" would more accurately communicate the scope of changes and help reviewers and future maintainers understand what this PR delivers at a glance.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch vm2benchmark-test2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
vm2/vm-runtime/src/move_vm_ext/warm_vm_cache.rs (1)

85-102: Fix type mismatch: PartialVMError cannot propagate to VMError in line 99.

The load_module call returns VMResult<...> (i.e., Result<..., PartialVMError>), but warm_vm_up returns Result<(), VMError>. The ? operator on line 99 cannot automatically convert PartialVMError to VMError because no From implementation exists between these types.

Given the comment states this is a "best effort approach and won't hurt if that fails," consider ignoring the error instead of propagating it:

let _ = vm.load_module(
    &ModuleId::new(CORE_CODE_ADDRESS, ident_str!("account").to_owned()),
    resolver,
);

Alternatively, map the error: .map_err(|_| VMError::...)? or change the return type to VMResult<()>.

chain/src/chain.rs (1)

2168-2174: Wrong “no-VM1-txns” state_root source

When VM1 is offline or there are no VM1 txns, state_root must be the parent VM1 root, not self.statedb.0 (current tip). Use the forked statedb or multi_state root.

-            BlockExecutedData {
-                state_root: self.statedb.0.state_root(),
+            BlockExecutedData {
+                state_root: statedb.state_root(),
                 txn_infos: vec![],
                 txn_events: vec![],
                 txn_table_infos: BTreeMap::new(),
                 write_sets: vec![],
             }
🧹 Nitpick comments (26)
rpc/client/src/remote_state_reader2/mod.rs (1)

63-73: Method works correctly but could be more idiomatic.

The implementation is functionally correct and properly handles all cases (error, no state, and successful balance retrieval). However, consider these improvements:

  1. Add documentation: This public method needs a doc comment explaining its purpose, parameters, return value, and potential errors.

  2. Refactor for readability: The map_or with a fallible closure (containing ?) is unusual. Consider using .transpose() for a more idiomatic conversion from Option<Result<T>> to Result<Option<T>>:

 pub fn get_balance2(&self, address: AccountAddress) -> Result<Option<u128>> {
     let state_key = StateKey::resource_typed::<BalanceResource>(&address)?;
-    self.client
-        .state_get_with_proof2(state_key)?
-        .state
-        .map_or(Ok(None), |state| {
-            Ok(Some(
-                bcs_ext::from_bytes::<BalanceResource>(state.0.as_slice())?.token(),
-            ))
-        })
+    self.client
+        .state_get_with_proof2(state_key)?
+        .state
+        .map(|state| {
+            bcs_ext::from_bytes::<BalanceResource>(state.0.as_slice())
+                .map(|balance_resource| balance_resource.token())
+        })
+        .transpose()
 }

Alternatively, a simple match expression would also improve clarity.

kube/manifest/starcoin-halley.yaml (2)

26-26: Use a more descriptive image tag instead of a commit SHA.

The image tag sha-6a5e4e8 (a Git commit short hash) lacks semantic meaning and traceability. For a benchmark release, consider using a descriptive tag such as vm2-benchmark-v1 or a semver version that clearly indicates the purpose and version of the image.

Commit SHAs make it difficult to understand what changes are deployed, complicate rollbacks, and reduce reproducibility in documentation and incident response.


50-57: Consider the implications of removing the readiness probe.

Per the AI summary, the readiness probe has been removed from this manifest. While the livenessProbe remains, there is no readinessProbe to verify that the starcoin service is fully initialized and ready to handle traffic (e.g., synced with peers, accepting RPC requests).

For a multi-replica (3-node) benchmark environment, a readiness probe can be valuable to ensure nodes are operational before the benchmark applies load. If the readiness probe removal was intentional, please document the rationale. If it was accidental, consider restoring it or adding a custom readiness check (e.g., an HTTP health endpoint).

rpc/server/src/module/debug_rpc.rs (1)

87-94: Document VM2-specific concurrency control.

These debug RPC methods now exclusively control VM2's concurrency level without indicating this in the API or documentation. Users calling set_concurrency_level and get_concurrency_level may not realize they're only affecting VM2.

Consider:

  • Adding documentation/comments indicating these methods control VM2 specifically
  • Adding a parameter to specify which VM to control (if dual-VM support is intended)
  • Ensuring VM1's concurrency is controllable through another mechanism (if needed)

Based on learnings: The set_concurrency_level_once() call is safe due to OnceCell semantics, which prevents race conditions.

vm2/vm-runtime/src/move_vm_ext/warm_vm_cache.rs (1)

78-80: Consider logging warm-up failures for observability.

The VM is returned to the caller even if warm_vm_up fails, which is reasonable for a "best effort" approach. However, silently skipping cache insertion could hide systematic issues (e.g., resolver problems, module loading failures) and cause performance degradation as new VMs are repeatedly created.

Consider adding a log statement to track warm-up failures:

 if Self::warm_vm_up(&vm, resolver).is_ok() {
     cache_locked.insert(id, vm.clone());
+} else {
+    log::debug!("Failed to warm up VM, skipping cache insertion");
 }

Alternatively, use a metrics counter to track cache insertion vs. skip rates for monitoring.

node/src/lib.rs (1)

140-144: Implementation verified and correct; optional documentation recommended.

Storage2 is properly registered in the registry during node initialization (node/src/node.rs lines 287-289), so the expect() call is safe and won't panic at runtime.

The implementation is sound. Consider adding a brief doc comment to clarify when to use storage2() versus storage():

+    /// Returns the VM2-backed storage instance for transaction execution and state access.
     pub fn storage2(&self) -> Arc<Storage2> {
         self.registry
             .get_shared_sync::<Arc<Storage2>>()
             .expect("Storage2 must exist.")
     }
chain/src/chain.rs (1)

2386-2389: k update should use the parent-derived epoch

self.epoch can be stale during execution on non-head parents. Use the epoch read from statedb2 above.

-        self.dag()
-            .ghost_dag_manager()
-            .update_k(self.epoch.max_uncles_per_block().try_into().unwrap());
+        self.dag()
+            .ghost_dag_manager()
+            .update_k(epoch.max_uncles_per_block().try_into().unwrap());

Would you confirm no other callers rely on self.epoch here?

sync/tests/full_build_and_execute_test.rs (4)

218-225: Use TxPoolService APIs to preserve metrics/invariants instead of inner.*.

Calling inner.import_txns/inner.get_pending bypasses timers and any service-level guards. Use add_txns_multi_signed and get_pending_txns.

-        txpool.inner.import_txns(
-            signed_transactions
-                .into_iter()
-                .map(MultiSignedUserTransaction::VM2)
-                .collect(),
-            false,
-            None,
-        )?;
+        txpool.add_txns_multi_signed(
+            signed_transactions
+                .into_iter()
+                .map(MultiSignedUserTransaction::VM2)
+                .collect(),
+            false,
+            None,
+        )?;
@@
-            let transactions = txpool
-                .inner
-                .get_pending(100, config.net().time_service().now_secs())?;
+            let transactions = txpool.get_pending_txns(
+                Some(100),
+                Some(config.net().time_service().now_secs()),
+            )?;

Also applies to: 228-234


29-35: Nit: fix parameter name typo.

Rename accoun_numberaccount_number for clarity.

-async fn create_account(
-    accoun_number: u32,
+async fn create_account(
+    account_number: u32,
@@
-    for _ in 0..accoun_number {
+    for _ in 0..account_number {

86-95: Reduce per-tx unlock overhead and remove index/unwrap panics.

Unlock the sender once per unique address and iterate with zip to avoid get(index).unwrap() panics; also keep address access consistent (.address() or field).

-    let mut signed_transactions = vec![];
-    for index in 0..receiver.len() {
-        let sender_address = sender.get(index).unwrap().address;
+    use std::collections::HashSet;
+    let mut signed_transactions = vec![];
+    let mut unlocked: HashSet<_> = HashSet::new();
+    for (s, r) in sender.iter().zip(receiver.iter()) {
+        let sender_address = *s.address();
@@
-        let transaction = build_batch_transfer_txn2(
-            sender.get(index).unwrap().address,
-            vec![receiver.get(index).unwrap().address],
+        let transaction = build_batch_transfer_txn2(
+            *s.address(),
+            vec![*r.address()],
@@
-        match account_service
-            .send(AccountRequest::UnlockAccount(
-                sender_address,
-                "".to_string(),
-                std::time::Duration::from_secs(100),
-            ))
-            .await??
-        {
-            AccountResponse::AccountInfo(_) => (),
-            _ => bail!("Unexpect response type."),
-        }
+        if unlocked.insert(sender_address) {
+            match account_service
+                .send(AccountRequest::UnlockAccount(
+                    sender_address,
+                    "".to_string(),
+                    std::time::Duration::from_secs(100),
+                ))
+                .await??
+            {
+                AccountResponse::AccountInfo(_) => (),
+                _ => bail!("Unexpected response type."),
+            }
+        }

Also applies to: 103-114, 114-125


39-41: Minor: error message wording.

“Unexpect response type.” → “Unexpected response type.” for clarity.

Also applies to: 56-57, 112-113, 122-123, 138-139

txpool/src/tx_pool_service_impl.rs (1)

397-409: Exposing Inner methods publicly bypasses service-level metrics and guards.

Making these pub allows callers to skip TxPoolService instrumentation (timers, error mapping) and invariants. Prefer keeping them pub(crate) and exposing wrapper methods on TxPoolService if external access is required.

Option A — revert visibility:

-    pub fn import_txns(
+    pub(crate) fn import_txns(
@@
-    pub fn get_pending(
+    pub(crate) fn get_pending(
@@
-    pub fn get_pool_client(&self) -> Result<PoolClient> {
+    pub(crate) fn get_pool_client(&self) -> Result<PoolClient> {

Option B — keep public but add doc comments stating they are for testing/bench and consider moving timers to Inner or adding cfg(test) gates.

Please confirm which external crates need these public methods so we can add proper TxPoolService forwards without exposing internals.

Also applies to: 420-439, 561-574

cmd/tx-factory/src/pressure_vm1.rs (1)

11-20: Consider consolidating parameters into a configuration struct.

The function takes 8 parameters, which can make call sites harder to read and maintain. Consider introducing a PressureTestConfig struct to group related parameters.

Example refactor:

pub struct PressureTestConfig {
    pub round_num: u32,
    pub account_num: u32,
    pub batch_size: u32,
    pub interval: Duration,
    pub transfer_account_size: usize,
    pub is_stress: bool,
}

pub fn start_vm1_pressure_test(
    mut tx_mocker: TxnMocker,
    config: PressureTestConfig,
    stopping_signal: Arc<AtomicBool>,
) -> std::thread::JoinHandle<()>
cmd/tx-factory/src/pressure_vm2.rs (1)

13-22: Consider consolidating parameters into a configuration struct.

The function takes 8 parameters, mirroring the VM1 implementation. Consider introducing a shared PressureTestConfig struct to improve maintainability and reduce duplication.

Example:

pub struct PressureTestConfig {
    pub round_num: u32,
    pub account_num: u32,
    pub batch_size: u32,
    pub interval: Duration,
    pub transfer_account_size: usize,
    pub is_stress: bool,
}

pub fn start_vm2_pressure_test(
    mut tx_mocker: TxnMocker,
    config: PressureTestConfig,
    stopping_signal: Arc<AtomicBool>,
) -> std::thread::JoinHandle<()>
vm2/account/src/account_manager.rs (1)

374-396: Address the TODO comment and document fail-fast behavior.

Line 380 contains a TODO about verifying the signer. Additionally, the method fails fast on the first locked account (line 383), returning an error without processing remaining transactions. While this is reasonable, it should be documented in the API to set clear expectations.

Do you want me to:

  1. Generate a verification script to check if there are existing validations for transaction signers elsewhere in the codebase?
  2. Help document the fail-fast behavior in the API documentation?
  3. Open an issue to track the TODO for signer validation?
cmd/tx-factory/src/main.rs (2)

186-190: Prefer CLI-level validation for vm_version

Use clap ValueEnum or possible_values to reject invalid vm_version at parse time instead of panicking at runtime.


108-138: Bound the wait loop and add context

Cap retries (e.g., max attempts or deadline), add backoff, and include why we’re waiting (ipc path, net, vm_version) to aid ops. Consider honoring a startup timeout flag.

cmd/tx-factory/src/txn_generator.rs (2)

44-62: Unify gas limits and avoid magic numbers

Gas caps vary (4,000,000 vs 40,000,000) across VM1/VM2 and mock/transfer/batch. If not intentional, align them via named constants and use the same defaults across generators.

Suggested pattern:

  • Define DEFAULT_MAX_GAS and DEFAULT_BATCH_MAX_GAS (per-VM if needed).
  • Replace numeric literals in all builders.
-            4000000,
+            DEFAULT_MAX_GAS_AMOUNT,
...
-            40_000_000,
+            DEFAULT_BATCH_MAX_GAS_AMOUNT,

Also applies to: 84-104, 128-148, 165-165


128-148: Consider clearer naming for batch builder

generate_account_txn2 builds a batch transfer; consider rename to generate_batch_transfer_txn2 for clarity.

cmd/tx-factory/src/mocker.rs (7)

48-72: Initialize and re-check sequence per path before generating txns.

new() seeds from VM2 only; VM1 gen may start from a wrong seq. Also sequences can drift due to concurrent submissions. Recheck at the start of gen methods.

Apply:

 pub fn gen_and_submit_txn2(&mut self, blocking: bool) -> Result<HashValue> {
-        let expiration_timestamp = self.fetch_expiration_time();
+        self.recheck_sequence_number2()?;
+        let expiration_timestamp = self.fetch_expiration_time();
         let raw_txn = self
             .generator
             .generate_mock_txn2(self.next_sequence_number, expiration_timestamp)?;
 pub fn gen_and_submit_txn(&mut self, blocking: bool) -> Result<HashValue> {
-        let expiration_timestamp = self.fetch_expiration_time();
+        self.recheck_sequence_number()?;
+        let expiration_timestamp = self.fetch_expiration_time();
         let raw_txn = self
             .generator
             .generate_mock_txn(self.next_sequence_number, expiration_timestamp)?;

Also applies to: 128-133, 167-172


263-270: Avoid cloning raw txns in batch signing; consume the vectors.

flat_map over iter() clones all RawUserTransaction(2). Use into_iter to move them.

Apply:

-        let signed_transactions = self.client.account_sign_txn_in_batch2(
-            txns.iter()
-                .flat_map(|(_, raw_txns)| raw_txns.clone())
-                .collect(),
-        )?;
+        let signed_transactions = self.client.account_sign_txn_in_batch2(
+            txns.into_iter().flat_map(|(_, raw_txns)| raw_txns).collect(),
+        )?;

And VM1:

-        let signed_transactions = self.client.account_sign_txn_in_batch(
-            txns.iter()
-                .flat_map(|(_, raw_txns)| raw_txns.clone())
-                .collect(),
-        )?;
+        let signed_transactions = self.client.account_sign_txn_in_batch(
+            txns.into_iter().flat_map(|(_, raw_txns)| raw_txns).collect(),
+        )?;

Also applies to: 294-301


767-790: Avoid per-sender Vec clone; use std::mem::take for zero‑copy move.

Cloning transactions per sender is wasteful. Move the Vec and reuse it.

Apply (pattern shown for VM2; mirror for VM1):

-        for (index, (sender_address, sequence_op)) in senders.iter().enumerate() {
+        for (index, (sender_address, sequence_op)) in senders.iter().enumerate() {
             ...
-            sender_transactions.push((*sender_address, transactions.clone()));
-            transactions.clear();
+            sender_transactions.push((*sender_address, std::mem::take(&mut transactions)));
         }

Also applies to: 805-829


83-87: Don’t unwrap RPC; return Result from get_factory_status.

Unwrap panics on transport/handler errors. Prefer propagating.

Apply:

-    pub fn get_factory_status(&self) -> bool {
-        self.client
-            .debug_txfactory_status(FactoryAction::Status)
-            .unwrap()
-    }
+    pub fn get_factory_status(&self) -> Result<bool> {
+        Ok(self.client.debug_txfactory_status(FactoryAction::Status)?)
+    }

76-82: Avoid expect() in fetch_expiration_time; propagate error.

A node_info failure will panic. Return Result and handle upstream.

Example:

-    fn fetch_expiration_time(&self) -> u64 {
-        let node_info = self
-            .client
-            .node_info()
-            .expect("node_info() should not failed");
-        node_info.now_seconds + DEFAULT_EXPIRATION_TIME
-    }
+    fn fetch_expiration_time(&self) -> Result<u64> {
+        let node_info = self.client.node_info()?;
+        Ok(node_info.now_seconds + DEFAULT_EXPIRATION_TIME)
+    }

Then adjust call sites to use ? and pass u64.


681-706: Graceful fallback on missing seq_numbers; avoid expect()s.

When seq from pool is None, you fetch from state and use expect(...). Prefer propagating with context to avoid panics during outages.

Example VM2 mapping:

-                None => {
-                    let state_reader = self
-                        .client
-                        .state_reader2(StateRootOption::Latest)
-                        .expect("state_reader error");
-                    let account_resource = state_reader
-                        .get_account_resource(address)
-                        .expect("get_account_resource error");
-                    let seq = account_resource.sequence_number();
-                    (address, Some(seq))
-                }
+                None => {
+                    let state_reader = self.client.state_reader2(StateRootOption::Latest)
+                        .context("state_reader2 failed")?;
+                    let seq = state_reader.get_account_resource(address)
+                        .context("get_account_resource (vm2) failed")?
+                        .sequence_number();
+                    (address, Some(seq))
+                }

Apply similar handling for VM1 path.

Also applies to: 708-733


607-609: Minor: prefer account_list.len() and clean log text.

Style/clarity nits.

Apply:

-        info!("{:?} accounts are created.", Vec::len(&account_list));
+        info!("{} accounts are created.", account_list.len());

Apply in both VM2 and VM1 create_accounts paths.

Also applies to: 677-679

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca1838b and 58ef230.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (37)
  • chain/src/chain.rs (5 hunks)
  • cmd/tx-factory/src/lib.rs (1 hunks)
  • cmd/tx-factory/src/main.rs (7 hunks)
  • cmd/tx-factory/src/mocker.rs (1 hunks)
  • cmd/tx-factory/src/pressure_vm1.rs (1 hunks)
  • cmd/tx-factory/src/pressure_vm2.rs (1 hunks)
  • cmd/tx-factory/src/txn_generator.rs (4 hunks)
  • commons/parallel-executor/src/executor.rs (3 hunks)
  • commons/parallel-executor/src/task.rs (1 hunks)
  • config/src/genesis_config/mod.rs (1 hunks)
  • config/src/lib.rs (1 hunks)
  • kube/manifest/starcoin-halley.yaml (2 hunks)
  • node/src/lib.rs (2 hunks)
  • rpc/api/src/txpool/mod.rs (2 hunks)
  • rpc/client/src/lib.rs (7 hunks)
  • rpc/client/src/remote_state_reader2/mod.rs (2 hunks)
  • rpc/server/Cargo.toml (1 hunks)
  • rpc/server/src/module/debug_rpc.rs (2 hunks)
  • rpc/server/src/module/txpool_rpc.rs (3 hunks)
  • scripts/release.sh (1 hunks)
  • sync/Cargo.toml (2 hunks)
  • sync/tests/full_build_and_execute_test.rs (1 hunks)
  • test-helper/src/lib.rs (1 hunks)
  • test-helper/src/node.rs (1 hunks)
  • txpool/src/tx_pool_service_impl.rs (3 hunks)
  • vm2/account/api/src/message.rs (3 hunks)
  • vm2/account/api/src/service.rs (3 hunks)
  • vm2/account/src/account_manager.rs (2 hunks)
  • vm2/executor/Cargo.toml (1 hunks)
  • vm2/executor/src/block_executor.rs (1 hunks)
  • vm2/framework/gas-meter/src/gas_meter.rs (0 hunks)
  • vm2/rpc/api/src/account_api.rs (2 hunks)
  • vm2/rpc/server/src/account_rpc.rs (2 hunks)
  • vm2/service/account/src/service.rs (1 hunks)
  • vm2/vm-runtime/src/move_vm_ext/warm_vm_cache.rs (3 hunks)
  • vm2/vm-runtime/src/parallel_executor/mod.rs (1 hunks)
  • vm2/vm-runtime/src/starcoin_vm.rs (1 hunks)
💤 Files with no reviewable changes (1)
  • vm2/framework/gas-meter/src/gas_meter.rs
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-09-15T13:00:33.502Z
Learnt from: lushengguo
PR: starcoinorg/starcoin#4659
File: executor/benchmark/src/vm_exec_benchmark.rs:434-435
Timestamp: 2025-09-15T13:00:33.502Z
Learning: In the Starcoin codebase, calling StarcoinVM::set_concurrency_level_once() after running serialized benchmarks is safe, as confirmed by the maintainer. The "once" semantics provide built-in protection against race conditions.

Applied to files:

  • rpc/server/src/module/debug_rpc.rs
📚 Learning: 2025-10-10T01:54:58.184Z
Learnt from: jackzhhuang
PR: starcoinorg/starcoin#4703
File: vm2/executor/src/block_executor.rs:73-81
Timestamp: 2025-10-10T01:54:58.184Z
Learning: In the Starcoin blockchain executor (both VM1 and VM2), committing state at intermediate transactions (e.g., first transaction with `index == 0`) is acceptable because if any subsequent transaction in the block is discarded, the entire block execution aborts and the block won't connect to the chain. Block-level atomicity is maintained at a higher level, ensuring partial blocks are never persisted.

Applied to files:

  • vm2/executor/src/block_executor.rs
  • chain/src/chain.rs
📚 Learning: 2025-09-01T03:56:58.362Z
Learnt from: welbon
PR: starcoinorg/starcoin#4633
File: vm/vm-runtime/Cargo.toml:48-48
Timestamp: 2025-09-01T03:56:58.362Z
Learning: In the Starcoin codebase, vm1 (starcoin-vm1-vm-runtime) may need to expose the same feature flags as vm2 to satisfy cross-version compatibility requirements when downstream projects like genesis depend on features from both versions. Empty feature declarations like `move-unit-test = []` may be intentionally added for compilation compatibility rather than to activate specific functionality.

Applied to files:

  • rpc/server/Cargo.toml
  • sync/Cargo.toml
📚 Learning: 2025-08-08T10:25:49.039Z
Learnt from: jackzhhuang
PR: starcoinorg/starcoin#4605
File: txpool/mock-service/src/lib.rs:114-120
Timestamp: 2025-08-08T10:25:49.039Z
Learning: In PR starcoinorg/starcoin#4605, txpool/mock-service/src/lib.rs: MockTxPoolService::next_sequence_number_with_header is currently unused; keeping todo!() in the mock is acceptable and won’t affect runtime unless invoked.

Applied to files:

  • cmd/tx-factory/src/mocker.rs
📚 Learning: 2025-09-30T01:46:29.197Z
Learnt from: jackzhhuang
PR: starcoinorg/starcoin#4703
File: chain/open-block/src/vm2.rs:44-45
Timestamp: 2025-09-30T01:46:29.197Z
Learning: In the Starcoin blockchain (file: chain/open-block/src/vm2.rs), block metadata transaction gas consumption is intentionally NOT counted toward the block gas limit (self.gas_used is not incremented for metadata transactions in initialize() method). Only user transactions in push_txns2() update self.gas_used.

Applied to files:

  • chain/src/chain.rs
🧬 Code graph analysis (20)
commons/parallel-executor/src/task.rs (1)
vm2/vm-runtime/src/parallel_executor/mod.rs (1)
  • is_block_meta_data (31-36)
vm2/vm-runtime/src/parallel_executor/mod.rs (1)
commons/parallel-executor/src/task.rs (1)
  • is_block_meta_data (28-30)
test-helper/src/lib.rs (1)
test-helper/src/node.rs (3)
  • run_node_by_config (17-22)
  • run_node_with_all_service (24-28)
  • run_test_node (12-15)
rpc/server/src/module/debug_rpc.rs (3)
vm2/vm-runtime/src/starcoin_vm.rs (2)
  • set_concurrency_level_once (1402-1407)
  • get_concurrency_level (1411-1416)
rpc/client/src/lib.rs (1)
  • get_concurrency_level (841-844)
rpc/api/src/debug/mod.rs (1)
  • get_concurrency_level (38-38)
vm2/rpc/api/src/account_api.rs (3)
vm2/account/api/src/service.rs (2)
  • sign_txn_in_batch (38-41)
  • sign_txn_in_batch (175-189)
vm2/account/src/account_manager.rs (1)
  • sign_txn_in_batch (374-396)
vm2/rpc/server/src/account_rpc.rs (2)
  • sign_txn_in_batch (174-185)
  • unlock_in_batch (207-223)
vm2/rpc/server/src/account_rpc.rs (5)
vm2/account/api/src/service.rs (2)
  • sign_txn_in_batch (38-41)
  • sign_txn_in_batch (175-189)
vm2/account/src/account_manager.rs (1)
  • sign_txn_in_batch (374-396)
vm2/rpc/api/src/account_api.rs (2)
  • sign_txn_in_batch (48-51)
  • unlock_in_batch (64-68)
rpc/client/src/lib.rs (1)
  • map_err (1323-1325)
vm2/rpc/server/src/lib.rs (1)
  • map_err (6-29)
test-helper/src/node.rs (2)
node/src/lib.rs (1)
  • config (128-132)
cmd/starcoin/src/cli_state.rs (1)
  • node_handle (130-132)
commons/parallel-executor/src/executor.rs (2)
commons/parallel-executor/src/task.rs (1)
  • init (56-56)
commons/parallel-executor/src/proptest_types/types.rs (1)
  • init (163-165)
rpc/api/src/txpool/mod.rs (4)
rpc/client/src/lib.rs (2)
  • submit_transactions2 (364-370)
  • next_sequence_number_in_batch2 (339-349)
rpc/server/src/module/txpool_rpc.rs (2)
  • submit_transactions2 (126-135)
  • next_sequence_number_in_batch2 (261-267)
cmd/tx-factory/src/mocker.rs (1)
  • next_sequence_number_in_batch2 (681-706)
txpool/src/pool/queue.rs (1)
  • addresses (558-571)
vm2/account/src/account_manager.rs (3)
vm2/account/api/src/service.rs (4)
  • unlock_account_in_batch (49-53)
  • unlock_account_in_batch (207-220)
  • sign_txn_in_batch (38-41)
  • sign_txn_in_batch (175-189)
vm2/rpc/api/src/account_api.rs (1)
  • sign_txn_in_batch (48-51)
vm2/rpc/server/src/account_rpc.rs (1)
  • sign_txn_in_batch (174-185)
vm2/account/api/src/service.rs (3)
vm2/account/src/account_manager.rs (3)
  • sign_txn_in_batch (374-396)
  • unlock_account (87-100)
  • unlock_account_in_batch (102-119)
vm2/rpc/api/src/account_api.rs (1)
  • sign_txn_in_batch (48-51)
vm2/rpc/server/src/account_rpc.rs (1)
  • sign_txn_in_batch (174-185)
cmd/tx-factory/src/main.rs (3)
cmd/tx-factory/src/pressure_vm1.rs (1)
  • start_vm1_pressure_test (11-59)
cmd/tx-factory/src/pressure_vm2.rs (1)
  • start_vm2_pressure_test (13-61)
rpc/client/src/lib.rs (4)
  • sleep (831-834)
  • new (136-141)
  • new (167-203)
  • new (1301-1320)
rpc/client/src/remote_state_reader2/mod.rs (1)
vm2/vm-types/src/state_store/state_key/mod.rs (1)
  • resource_typed (143-145)
rpc/client/src/lib.rs (3)
cmd/tx-factory/src/mocker.rs (1)
  • next_sequence_number_in_batch2 (681-706)
rpc/api/src/txpool/mod.rs (2)
  • next_sequence_number_in_batch2 (86-89)
  • submit_transactions2 (29-32)
rpc/server/src/module/txpool_rpc.rs (2)
  • next_sequence_number_in_batch2 (261-267)
  • submit_transactions2 (126-135)
cmd/tx-factory/src/txn_generator.rs (2)
cmd/tx-factory/src/mocker.rs (2)
  • new (38-72)
  • sequence_number (736-754)
testsuite/tests/steps/transaction.rs (1)
  • transfer_txn (83-113)
cmd/tx-factory/src/mocker.rs (5)
rpc/client/src/lib.rs (9)
  • sync_status (1185-1188)
  • new (136-141)
  • new (167-203)
  • new (1301-1320)
  • state_reader (606-611)
  • node_info (255-258)
  • account_list (411-414)
  • next_sequence_number_in_batch2 (339-349)
  • next_sequence_number_in_batch (331-337)
cmd/tx-factory/src/txn_generator.rs (1)
  • new (28-42)
txpool/src/tx_pool_service_impl.rs (5)
  • new (41-83)
  • next_sequence_number (193-202)
  • next_sequence_number (463-472)
  • next_sequence_number_in_batch (204-237)
  • next_sequence_number_in_batch (474-501)
vm2/account/src/account_manager.rs (2)
  • new (67-74)
  • unlock_account (87-100)
txpool/src/pool/queue.rs (1)
  • addresses (558-571)
sync/tests/full_build_and_execute_test.rs (4)
config/src/lib.rs (6)
  • temp_dir (110-113)
  • default (153-155)
  • default (281-283)
  • path (263-265)
  • load_with_opt (294-319)
  • load_with_opt (549-552)
test-helper/src/node.rs (1)
  • run_node_with_all_service (24-28)
vm2/account/api/src/service.rs (2)
  • create_account (17-17)
  • create_account (93-100)
node/src/lib.rs (19)
  • config (128-132)
  • storage2 (140-144)
  • new (78-92)
  • registry (120-122)
  • registry (129-130)
  • registry (135-136)
  • registry (141-142)
  • registry (147-148)
  • registry (153-153)
  • registry (157-158)
  • registry (163-163)
  • registry (167-167)
  • registry (171-171)
  • registry (190-191)
  • registry (196-197)
  • registry (203-203)
  • registry (214-214)
  • registry (215-215)
  • txpool (189-193)
chain/src/chain.rs (2)
vm2/executor/src/block_executor.rs (1)
  • block_execute (37-99)
executor/src/block_executor.rs (1)
  • block_execute (65-147)
rpc/server/src/module/txpool_rpc.rs (4)
rpc/server/src/module/mod.rs (1)
  • convert_to_rpc_error (69-72)
types/src/multi_transaction.rs (1)
  • id (89-94)
rpc/api/src/txpool/mod.rs (2)
  • submit_transactions2 (29-32)
  • next_sequence_number_in_batch2 (86-89)
rpc/client/src/lib.rs (2)
  • submit_transactions2 (364-370)
  • next_sequence_number_in_batch2 (339-349)
vm2/service/account/src/service.rs (3)
block-relayer/src/block_relayer.rs (1)
  • txns (164-164)
network-rpc/src/rpc.rs (1)
  • txns (101-101)
rpc/api/src/types.rs (2)
  • txns (846-851)
  • txns (861-863)
🔇 Additional comments (32)
vm2/vm-runtime/src/starcoin_vm.rs (1)

321-332: Correct log message for max gas unit price bound violation.

The log message on line 324 now correctly references "max" to match the upper bound violation being checked. This aligns with the GAS_UNIT_PRICE_ABOVE_MAX_BOUND status code and improves consistency with the analogous minimum bound check (lines 308–318).

rpc/server/Cargo.toml (1)

79-79: VM2 runtime dependency correctly added.

The new starcoin-vm2-vm-runtime dependency aligns with the PR objectives to add VM2 benchmark and batch operation support. The configuration follows the established pattern (using workspace = true) and is properly placed under the Move 2.0 dependencies section.

Verify that this dependency is properly utilized in the RPC server implementation (specifically in batch transaction handling and VM2 integration code that is outside the scope of this Cargo.toml file).

vm2/executor/src/block_executor.rs (1)

6-6: Logging import added consistently with dependency.

The import of info from starcoin_logger::prelude aligns with the new starcoin-logger dependency added in Cargo.toml. While the macro is not invoked in the visible code, this is consistent with the PR's goal of adding logging enhancements across VM2 components.

vm2/executor/Cargo.toml (1)

22-22: Dependency addition follows established patterns.

The starcoin-logger dependency is added with workspace = true, maintaining consistency with all other dependencies in this section. This enables the logging import in block_executor.rs.

config/src/genesis_config/mod.rs (1)

241-246: LGTM! Public constructor improves API flexibility.

Making CustomNetworkID::new public is a reasonable API expansion that provides a direct construction method alongside the existing FromStr implementation, improving usability for programmatic construction.

config/src/lib.rs (1)

509-516: LGTM! Consistent with existing test config constructors.

The new_for_halley() constructor follows the same pattern as random_for_test() and provides a convenient way to create a NodeConfig for the Halley test network.

test-helper/src/lib.rs (1)

24-24: LGTM! Export correctly adds new test helper to public API.

The addition of run_node_with_all_service to the public exports is necessary and follows the established pattern for the test-helper module's facade.

test-helper/src/node.rs (1)

24-28: LGTM! Provides useful alternative node startup for tests.

This function complements run_node_by_config by launching the node with all services active (including the pacemaker), whereas run_node_by_config stops the pacemaker after launch. This distinction is valuable for tests that need the full node running versus those requiring controlled block production.

rpc/client/src/remote_state_reader2/mod.rs (1)

15-15: LGTM: Import added for VM2 balance retrieval.

The BalanceResource import is necessary for the new get_balance2 method and is correctly placed within the existing starcoin_vm2_types imports.

kube/manifest/starcoin-halley.yaml (2)

1-33: Clarify the relationship between this K8s manifest and the VM2 benchmark PR objectives.

This Kubernetes StatefulSet manifest appears to be infrastructure configuration rather than code implementing VM2 benchmarking, transaction factory infrastructure, or batch operations. The PR objectives emphasize VM2 transaction execution benchmarks and batch transaction operations, but this file modifies only deployment and scaling parameters.

Please clarify:

  • Is this manifest update intended to support the new VM2 benchmark code (e.g., as a test deployment)?
  • Should this infrastructure change be included in the same PR as the code changes, or should it be a separate operational PR?
  • Is there a specific dependency or ordering requirement?

14-14: Justify the replica scaling change from 1 to 3.

Increasing replicas from 1 to 3 is a significant operational change that affects network topology, consensus, peer discovery, and resource allocation. The PR description does not explain this change or its relationship to the VM2 benchmark objectives.

Clarify whether this scaling is necessary for pressure testing, whether 3 is the intended target, and whether this should remain in the same PR as the code changes or be managed separately.

rpc/server/src/module/debug_rpc.rs (1)

16-16: Verify whether VM1 concurrency control removal is intentional.

The import change removes access to VM1's concurrency level while VM1 remains active in the system:

  • VM1 capability exists: vm/vm-runtime/src/starcoin_vm.rs contains set_concurrency_level_once() and get_concurrency_level() methods
  • VM1 is still used: Txpool and state management handle both MultiSignedUserTransaction::VM1 and ::VM2 separately
  • VM2 is now exclusive: Debug RPC calls only StarcoinVM2::set_concurrency_level_once() and ::get_concurrency_level()
  • No alternative mechanism: No other RPC method or code path provides runtime control of VM1 concurrency
  • No documentation: No comments or deprecation notices explain the change

Confirm with the PR author:

  • Is this intentional (VM1 being phased out)?
  • Should both VMs have independent runtime concurrency control?
  • If VM1 control is no longer needed, document the architectural decision
node/src/lib.rs (1)

20-20: LGTM! Import addition supports VM2 infrastructure.

The addition of Storage2 to the public imports is necessary for the new storage2() accessor and aligns with the VM2 support being introduced in this PR.

vm2/vm-runtime/src/parallel_executor/mod.rs (1)

31-36: Good hook implementation for BlockMetadata detection

Enum match is correct and aligns with the new Transaction::is_block_meta_data API.

Please ensure unit tests cover a block with BlockMetadata first and none-second to assert scheduling behavior.

commons/parallel-executor/src/task.rs (1)

26-30: Sane default for new API; preserves back-compat

The default false keeps existing implementors compiling. No issues.

commons/parallel-executor/src/executor.rs (2)

357-366: Nice: pre-running BlockMetadata reduces contention

Good placement before thread pool spawn; retains parallelism for remaining txns.

If scheduler invariants ever change, the guard code above will keep this safe; consider adding a unit test asserting (0,0) ordering for a block with metadata first.


14-14: Import is fine

error! macro import is used in new guard paths.

sync/Cargo.toml (1)

62-62: VM2 dev-deps addition looks fine; verify they remain dev-only.

Confirm these crates are needed only for tests/benchmarks and won’t be pulled into release via workspace default-members. Also ensure required feature flags for VM2 crates are aligned across vm1/vm2 to avoid feature-gating build breaks (e.g., mirroring empty feature placeholders when needed). Based on learnings.

Also applies to: 75-79

cmd/tx-factory/src/lib.rs (1)

1-3: LGTM — modules exported cleanly.

No issues spotted with the added module declarations.

scripts/release.sh (1)

8-8: No changes needed—binary name is explicitly defined in Cargo.toml.

The tx-factory package explicitly specifies [[bin]] name = "starcoin_txfactory" in its Cargo.toml. Cargo respects explicit binary names exactly as written; it does not automatically convert underscores to hyphens. The original script correctly references starcoin_txfactory and will work as intended. No guard or fallback is required.

Likely an incorrect or invalid review comment.

vm2/rpc/server/src/account_rpc.rs (2)

174-185: LGTM: Batch signing implementation follows established patterns.

The implementation correctly delegates to the account service and maintains consistency with other RPC methods in this file.


207-223: LGTM: Batch unlock implementation is consistent.

The duration conversion and async handling follow the same pattern as the single-account unlock method.

vm2/account/api/src/message.rs (1)

25-27: LGTM: Batch operation message variants are well-structured.

The new enum variants follow established patterns and use appropriate types for batch operations.

Also applies to: 36-36, 67-67

cmd/tx-factory/src/pressure_vm1.rs (1)

21-59: LGTM: Thread-based pressure test implementation is sound.

The loop correctly checks the stopping signal, handles errors appropriately, and provides clear logging for debugging.

vm2/rpc/api/src/account_api.rs (1)

47-51: LGTM: Batch signing API is correctly defined.

The method signature and RPC name follow the established VM2 API conventions.

rpc/api/src/txpool/mod.rs (1)

28-32: LGTM: Batch transaction pool APIs are well-defined.

The new batch methods follow established patterns and use appropriate VM2 types.

Also applies to: 83-89

vm2/service/account/src/service.rs (1)

193-200: LGTM: Batch request handling is correctly implemented.

The service properly delegates to the account manager and returns the appropriate response types.

vm2/account/src/account_manager.rs (1)

102-119: LGTM: Batch unlock implementation is consistent and correct.

The method properly handles each account in the batch, maintaining consistency with the single-account unlock flow.

rpc/server/src/module/txpool_rpc.rs (2)

261-267: Thin pass-through looks correct

next_sequence_number_in_batch2 forwards to service.next_sequence_number2_in_batch as expected.


126-135: Good reuse via MultiSignedUserTransaction mapping

Delegating VM2 batch submit to submit_transactions_multi keeps logic unified. Client wrapper parity confirmed: submit_transactions2 is properly exposed at rpc/client/src/lib.rs (lines 364–368).

vm2/account/api/src/service.rs (1)

38-42: API additions look good

New batch methods on the async service trait are coherent with existing single-item APIs.

Also applies to: 49-54

cmd/tx-factory/src/mocker.rs (1)

637-643: Unify account funding amounts between VM1 and VM2 batch calls; parameter order is correct, but amounts differ inconsistently.

The parameter order in both generate_account_txn and generate_account_txn2 is consistent across all calls: (sequence_number, sender, receivers, amount, gas_price, expiration_timestamp). However, funding amounts are inconsistent:

  • generate_account_txn (line 636, main batch): amount=1_000_000_000 (INITIAL_BALANCE), gas_price=1
  • generate_account_txn (line 660, tail batch): amount=1, gas_price=10_000
  • generate_account_txn2 (line 566, batch): amount=1, gas_price=1 ← should match VM1's main batch
  • generate_account_txn2 (line 590, tail batch): amount=1, gas_price=10_000

VM2's main batch (line 566) should use INITIAL_BALANCE (1_000_000_000) instead of 1 to align with VM1's batch funding strategy. Verify this is intentional or unify both to use INITIAL_BALANCE.

Comment on lines +108 to +138
fn get_account_or_default2(
client: &RpcClient,
account_address: Option<AccountAddress2>,
) -> Result<AccountInfo2> {
let account = match account_address {
None => {
let mut default_account = client.account_default2()?;
while default_account.is_none() {
std::thread::sleep(Duration::from_millis(1000));
default_account = client.account_default2()?;
}

// let addr = default_account.clone().unwrap().address;
// let state_reader = client.state_reader2(StateRootOption::Latest)?;
// let mut balance = state_reader.get_balance2(addr)?;
// // balance resource has not been created
// while balance.is_none() {
// std::thread::sleep(Duration::from_millis(1000));
// balance = state_reader.get_balance2(addr)?;
// info!("account balance is null.");
// }
default_account.unwrap()
}
Some(a) => match client.account_get2(a)? {
None => bail!("the specified account does not exists in the starcoin node"),
Some(w) => w,
},
};
info!("get_account_or_default for vm2: {}", account.address);
Ok(account)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Unconditional VM2 dependency can hang VM1 runs; gate by vm_version and decouple TxnMocker

  • get_account_or_default2 busy-waits before the ctrlc handler is installed; with vm_version=1 this can loop forever if VM2 has no default account.
  • TxnMocker::new requires VM2 args even for VM1 path, forcing the above dependency.

Action:

  • Make VM2 account fetch/existence conditional on vm_version==2, and adjust TxnMocker to accept Option<…> for VM2 (or provide separate constructors).
  • Add bounded retries or a timeout to get_account_or_default2 to avoid indefinite startup hangs.

Also applies to: 205-226, 244-268

🤖 Prompt for AI Agents
In cmd/tx-factory/src/main.rs around lines 108-138 (also apply same changes at
205-226 and 244-268): currently get_account_or_default2 unconditionally
busy-waits for a VM2 default account and TxnMocker::new requires VM2 args even
when vm_version==1; change code so VM2 account fetching only runs when
vm_version == 2, refactor TxnMocker to accept an Option for the VM2 account (or
add a separate Vm1 constructor) so VM1 paths do not construct or block on VM2
resources, and modify get_account_or_default2 to use bounded retries or a
configurable timeout (returning an error if no account found after N attempts)
instead of looping indefinitely so startup cannot hang.

Comment on lines 187 to 196
let chain_reader_service = registry.service_ref::<ChainReaderService>().await?;
loop {
// generate_block.notify(DeterminedDagBlock)?;
let current_header = get_current_header(chain_reader_service.clone()).await?;
info!("jacktest: current_header: {}", current_header.number());
if current_header.number() > 300 {
break;
}
tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Avoid unbounded busy-wait loops; add a timeout and backoff.

Both loops can spin indefinitely. Add a deadline and small sleep/backoff in the second loop to reduce CPU burn and avoid hangs.

Apply this diff minimally:

@@
-        loop {
+        let deadline = std::time::Instant::now() + std::time::Duration::from_secs(120);
+        loop {
             // generate_block.notify(DeterminedDagBlock)?;
             let current_header = get_current_header(chain_reader_service.clone()).await?;
             info!("jacktest: current_header: {}", current_header.number());
             if current_header.number() > 300 {
                 break;
             }
+            if std::time::Instant::now() >= deadline {
+                bail!("timed out waiting for block height > 300");
+            }
             tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
         }
@@
-        loop {
+        loop {
             let transactions = txpool
                 .inner
                 .get_pending(100, config.net().time_service().now_secs())?;
             if transactions.is_empty() {
                 break;
             }
+            tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
         }

Also applies to: 227-234

🤖 Prompt for AI Agents
In sync/tests/full_build_and_execute_test.rs around lines 187 to 196 (and
similarly for lines 227 to 234), the loop is an unbounded busy-wait that can
spin forever and burn CPU; add a deadline and a small backoff to avoid hangs.
Implement a timeout/deadline (e.g., record Instant::now() and bail with a test
failure or return Err after a configurable duration like 30s) and add a short
sleep inside the loop (e.g., 100–500ms) or exponential backoff between
iterations; use await-friendly tokio::time::sleep for the backoff and check the
elapsed time each iteration to break with a clear error when the deadline is
exceeded. Ensure both loops get the same treatment and return/propagate the
failure so tests fail fast instead of hanging.

Comment on lines +175 to +189
async fn sign_txn_in_batch(
&self,
raw_txn: Vec<RawUserTransaction>,
) -> Result<Vec<SignedUserTransaction>> {
let response = self
.send(AccountRequest::SignTxnInBatch { txns: raw_txn })
.await??;
if let AccountResponse::SignedTxnList(txns) = response {
Ok(txns)
} else {
return Err(anyhow::anyhow!(
"unexpected response type: expected SignedTxnList"
));
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Avoid panics; return typed errors consistently in batch methods

  • sign_txn_in_batch correctly returns Err on unexpected responses.
  • unlock_account_in_batch still panics; align with the above and other non-panicking paths.

Apply this change:

-        } else {
-            panic!("Unexpect response type.")
-        }
+        } else {
+            return Err(anyhow::anyhow!("unexpected response type: expected AccountList"));
+        }

Additionally, prefer anyhow::Context on the .send(...).await?? chain for richer diagnostics.

Also applies to: 207-220

Comment on lines +62 to +68
/// unlock accounts for duration in seconds, default to u32::max.
#[rpc(name = "account.unlock_in_batch2")]
fn unlock_in_batch(
&self,
batch: Vec<(AccountAddress, String)>,
duration: Option<u32>,
) -> FutureResult<Vec<AccountInfo>>;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix RPC naming inconsistency.

The RPC name uses account.unlock_in_batch2 while other methods in this file use the account2.* prefix pattern (e.g., account2.sign_txn_in_batch on line 47). This inconsistency could cause confusion for API consumers.

Apply this diff to fix the naming:

-    #[rpc(name = "account.unlock_in_batch2")]
+    #[rpc(name = "account2.unlock_in_batch")]
     fn unlock_in_batch(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// unlock accounts for duration in seconds, default to u32::max.
#[rpc(name = "account.unlock_in_batch2")]
fn unlock_in_batch(
&self,
batch: Vec<(AccountAddress, String)>,
duration: Option<u32>,
) -> FutureResult<Vec<AccountInfo>>;
/// unlock accounts for duration in seconds, default to u32::max.
#[rpc(name = "account2.unlock_in_batch")]
fn unlock_in_batch(
&self,
batch: Vec<(AccountAddress, String)>,
duration: Option<u32>,
) -> FutureResult<Vec<AccountInfo>>;
🤖 Prompt for AI Agents
In vm2/rpc/api/src/account_api.rs around lines 62 to 68, the #[rpc(name =
"...")] attribute incorrectly uses "account.unlock_in_batch2" while other RPCs
use the "account2.*" prefix; change the RPC name to "account2.unlock_in_batch2"
so it matches the file's naming convention (update the attribute value only).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
vm2/vm-runtime/src/starcoin_vm.rs (1)

752-770: Remove temporary debug logs before merge.

These info! logs with "jacktest" prefixes are temporary debugging scaffolding used to trace prologue execution flow. They should be removed before merging to a production branch.

Apply this diff to remove the temporary logs:

-        info!("StarcoinVM::run_prologue | jacktest: 0");
         let (payload_type, script_or_package_hash, package_address) = match txn_data.payload() {
             TransactionPayloadMetadata::Script(hash) => {
                 (TransactionPayloadType::Script, *hash, AccountAddress::ZERO)
             }
             TransactionPayloadMetadata::Package(hash, package_address) => {
                 (TransactionPayloadType::Package, *hash, *package_address)
             }
             TransactionPayloadMetadata::ScriptFunction => (
                 TransactionPayloadType::ScriptFunction,
                 HashValue::zero(),
                 AccountAddress::ZERO,
             ),
         };
-        info!("StarcoinVM::run_prologue | jacktest: 1");
 
         let traversal_storage = TraversalStorage::new();
         // Run prologue by genesis account
-        info!("StarcoinVM::run_prologue | jacktest: 2, txn_gas_price: {}, txn_max_gas_amount: {}", txn_gas_price, txn_max_gas_amount);
♻️ Duplicate comments (3)
sync/tests/full_build_and_execute_test.rs (3)

3-3: Fix invalid import: anyhow::Ok does not exist.
Remove Ok from the use list.

-use anyhow::{bail, format_err, Ok, Result};
+use anyhow::{bail, format_err, Result};

169-180: Use the intended genesis_config when loading NodeConfig.
You set opt.genesis_config but call NodeConfig::load_with_opt(&global_opt), which ignores it.

Minimal fix (reuse opt):

-    let node_config = Arc::new(NodeConfig::load_with_opt(&global_opt)?);
+    let node_config = Arc::new(NodeConfig::load_with_opt(&opt)?);

201-216: Add deadlines to loops to avoid indefinite hangs.
All three loops can spin forever; add a timeout and keep the backoff.

@@
-        loop {
+        let deadline = std::time::Instant::now() + std::time::Duration::from_secs(120);
+        loop {
             let current_header = get_current_header(chain_reader_service.clone()).await?;
             let default_account_balance = match get_balance(default_account.address, storage1.clone(), storage2.clone(), current_header.id()).await {
                 std::result::Result::Ok(balance) => balance,
                 Err(e) => {
                     info!("get balance error: {} and waiting for the token initialization", e);
                     tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
                     continue;
                 }
             };
             if default_account_balance > account_count as u128 * initial_balance + initial_gas_fee { // get enough token to pay for gas
                 break;
             }
+            if std::time::Instant::now() >= deadline {
+                bail!("timed out waiting for initial balance");
+            }
             tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
         }
@@
-        loop {
+        let deadline = std::time::Instant::now() + std::time::Duration::from_secs(120);
+        loop {
             let transactions = txpool
                 .inner
                 .get_pending(100, config.net().time_service().now_secs())?;
             if transactions.is_empty() {
                 break;
             }
+            if std::time::Instant::now() >= deadline {
+                bail!("timed out waiting for txpool to drain (phase 1)");
+            }
             tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
         }
@@
-        loop {
+        let deadline = std::time::Instant::now() + std::time::Duration::from_secs(120);
+        loop {
             let transactions = txpool
                 .inner
                 .get_pending(100, config.net().time_service().now_secs())?;
             if transactions.is_empty() {
                 break;
             }
+            if std::time::Instant::now() >= deadline {
+                bail!("timed out waiting for txpool to drain (phase 2)");
+            }
             tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
         }

Also applies to: 247-255, 282-290

🧹 Nitpick comments (4)
sync/tests/full_build_and_execute_test.rs (4)

88-127: Safer iteration: avoid indexing/unwrap; zip senders/receivers.
Reduces panic risk and clarifies flow.

-    let mut signed_transactions = vec![];
-    for index in 0..receiver.len() {
-        let sender_address = sender.get(index).unwrap().address;
-        let next_seq = *next_seq_map.get(&sender_address).unwrap();
-        next_seq_map
-            .entry(sender_address)
-            .and_modify(|next_seq| *next_seq += 1);
-        let transaction = build_batch_transfer_txn2(
-            sender.get(index).unwrap().address,
-            vec![receiver.get(index).unwrap().address],
-            next_seq,
-            amount,
-            2,
-            40_000_00,
-            expire_time,
-            header_block.chain_id().id().into(),
-        );
+    let mut signed_transactions = Vec::with_capacity(receiver.len());
+    for (s, r) in sender.iter().zip(receiver.iter()) {
+        let sender_address = s.address;
+        let next_seq = *next_seq_map
+            .get(&sender_address)
+            .expect("next sequence must be initialized");
+        next_seq_map.entry(sender_address).and_modify(|seq| *seq += 1);
+        let transaction = build_batch_transfer_txn2(
+            sender_address,
+            vec![r.address],
+            next_seq,
+            amount,
+            2,
+            40_000_00,
+            expire_time,
+            header_block.chain_id().id().into(),
+        );
         match account_service
             .send(AccountRequest::UnlockAccount(
                 sender_address,
                 "".to_string(),
                 std::time::Duration::from_secs(100),
             ))
             .await??
         {
-            AccountResponse::AccountInfo(_) => (),
-            _ => bail!("Unexpect response type."),
+            AccountResponse::AccountInfo(_) => (),
+            _ => bail!("Unexpected response type."),
         }
         let signed_transaction = match account_service
             .send(AccountRequest::SignTxn {
                 txn: Box::new(transaction),
                 signer: sender_address,
             })
             .await??
         {
-            AccountResponse::SignedTxn(signed_transction) => *signed_transction,
-            _ => bail!("Unexpect response type."),
+            AccountResponse::SignedTxn(s) => *s,
+            _ => bail!("Unexpected response type."),
         };
         signed_transactions.push(signed_transaction);
     }

Optional: unlock each unique sender once before the loop to avoid repeated unlocks.


152-154: Mark long-running benchmark as ignored.
Prevents default cargo test from running this heavy test.

-// #[ignore = "This is a benchmark test, not a unit test"]
-#[test]
+#[ignore = "This is a benchmark test, not a unit test"]
+#[test]
 fn test_full_build_and_execute_in_custom_network() -> Result<()> {

183-184: Set VM concurrency level once, early.
Call it before submitting work; remove the later call.

-    // StarcoinVM::set_concurrency_level_once(num_cpus::get());
+    StarcoinVM::set_concurrency_level_once(num_cpus::get());
@@
-        StarcoinVM::set_concurrency_level_once(num_cpus::get());
+        // already set above

Also applies to: 257-257


31-48: Minor nits: typos and message text.

  • Rename accoun_numberaccount_number.
  • “Unexpect response type.” → “Unexpected response type.”
  • Optional: rename signed_transction binding → signed_txn.

Also applies to: 50-62, 113-115, 124-125, 140-141

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58ef230 and f60b61f.

📒 Files selected for processing (6)
  • config/src/txpool_config.rs (1 hunks)
  • sync/tests/full_build_and_execute_test.rs (1 hunks)
  • vm2/aggregator/src/aggregator_v1_extension.rs (2 hunks)
  • vm2/vm-runtime/src/errors.rs (1 hunks)
  • vm2/vm-runtime/src/starcoin_vm.rs (3 hunks)
  • vm2/vm-runtime/src/verifier/transaction_arg_validation.rs (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-14T15:08:48.415Z
Learnt from: jackzhhuang
PR: starcoinorg/starcoin#4648
File: miner/tests/miner_test.rs:87-93
Timestamp: 2025-09-14T15:08:48.415Z
Learning: In Starcoin test files (like miner/tests/miner_test.rs), jackzhhuang prefers to use std::thread::sleep for intentional blocking delays in test scenarios, even within actor event handlers, rather than using non-blocking ctx.run_later alternatives.

Applied to files:

  • sync/tests/full_build_and_execute_test.rs
🧬 Code graph analysis (1)
sync/tests/full_build_and_execute_test.rs (6)
config/src/lib.rs (6)
  • temp_dir (110-113)
  • default (153-155)
  • default (281-283)
  • path (263-265)
  • load_with_opt (294-319)
  • load_with_opt (549-552)
test-helper/src/node.rs (1)
  • run_node_with_all_service (24-28)
vm2/account/api/src/service.rs (2)
  • create_account (17-17)
  • create_account (93-100)
node/src/lib.rs (18)
  • config (128-132)
  • storage2 (140-144)
  • new (78-92)
  • registry (120-122)
  • registry (129-130)
  • registry (135-136)
  • registry (141-142)
  • registry (147-148)
  • registry (153-153)
  • registry (157-158)
  • registry (163-163)
  • registry (167-167)
  • registry (171-171)
  • registry (190-191)
  • registry (196-197)
  • registry (203-203)
  • registry (214-214)
  • registry (215-215)
vm2/vm-runtime/src/starcoin_vm.rs (3)
  • new (87-110)
  • new (113-136)
  • set_concurrency_level_once (1405-1410)
vm2/service/account/src/service.rs (1)
  • registry (222-222)
🔇 Additional comments (3)
vm2/aggregator/src/aggregator_v1_extension.rs (1)

13-13: Verify the logging import is intentional.

The import is valid, but ensure this is part of the intended logging strategy for VM2 and not just supporting the debug statement at line 372.

vm2/vm-runtime/src/verifier/transaction_arg_validation.rs (1)

20-20: LGTM: Logging import added.

The import is correctly added to support runtime visibility.

vm2/vm-runtime/src/starcoin_vm.rs (1)

322-332: LGTM! Fixed error message for gas price validation.

The warning message now correctly references "max" instead of "min" when the gas unit price exceeds the maximum bound, making the error message consistent with the validation logic.

Comment on lines 61 to 63
pub fn max_count(&self) -> u64 {
self.max_count.unwrap_or(4096)
self.max_count.unwrap_or(40960000)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the 10,000x increase in default max_count and update outdated documentation.

The default transaction pool size has been increased from 4,096 to 40,960,000 (approximately 40 million), which is a 10,000x increase. This raises several concerns:

  1. Outdated documentation: Line 18 still states "default to 4096"
  2. Memory exhaustion risk: 40 million transactions could consume tens of gigabytes of memory, potentially exceeding available system resources
  3. Performance degradation: Operations on a pool this large (lookups, iteration, eviction, transaction selection) will be significantly slower
  4. Inconsistency with memory limits: The max_mem_usage limit may be exceeded before reaching 40 million transactions, making this count limit ineffective

If this change is intended for VM2 benchmark testing, consider:

  • Using a test-specific configuration instead of modifying production defaults
  • Adding a separate benchmark config profile
  • Documenting why such a large value is necessary
  • Choosing a more reasonable production default (e.g., 40,960 for a 10x increase instead)

Please confirm whether this massive increase is intentional for production use, and if so, update the documentation on line 18 and add comments explaining the rationale.



---

🏁 Script executed:

```shell
#!/bin/bash
# View the file content around the relevant lines
echo "=== File content: config/src/txpool_config.rs ==="
cat config/src/txpool_config.rs | head -80

Length of output: 3057


🏁 Script executed:

#!/bin/bash
# Search for related test or benchmark configurations
echo "=== Searching for test or benchmark txpool configs ==="
fd -t f "txpool" -x grep -l "config\|benchmark\|test" {} \;

Length of output: 120


🏁 Script executed:

#!/bin/bash
# Search for any documentation or comments about transaction pool sizing
echo "=== Searching for txpool documentation ==="
find . -type f \( -name "*.md" -o -name "*.rs" \) -exec grep -l "max_count\|transaction pool" {} \; | head -20

Length of output: 233


🏁 Script executed:

#!/bin/bash
# Search for references to the 40960000 value to understand context
echo "=== Searching for references to 40960000 ==="
rg -n "40960000" --type rs

Length of output: 71


🏁 Script executed:

#!/bin/bash
# Search for max_mem_usage to understand memory limits
echo "=== Checking max_mem_usage configuration ==="
rg -n -A5 "max_mem_usage" config/src/txpool_config.rs

Length of output: 1162


🏁 Script executed:

#!/bin/bash
# Check test-helper txpool configuration
echo "=== test-helper txpool config ==="
cat test-helper/src/txpool.rs | head -100

Length of output: 3228


🏁 Script executed:

#!/bin/bash
# Search for transaction size or pool documentation
echo "=== Searching for transaction pool implementation details ==="
rg -n -C3 "struct.*Transaction" txpool/src/ -t rs | head -50

Length of output: 88


🏁 Script executed:

#!/bin/bash
# Look for any size or memory-related calculations
echo "=== Searching for transaction sizing ==="
rg -n "size_of\|len\(\)\|sizeof" txpool/src/ -t rs | head -30

Length of output: 67


🏁 Script executed:

#!/bin/bash
# Search for any comments or documentation about 40960000 or large numbers
echo "=== Searching for large number references in txpool ==="
rg -n "[0-9]{7,}" txpool/ config/ -t rs | grep -v "test\|mock"

Length of output: 82


🏁 Script executed:

#!/bin/bash
# Check if there are separate config files for different environments
echo "=== Looking for environment-specific configs ==="
fd -t f "config" config/ | head -20

Length of output: 725


🏁 Script executed:

#!/bin/bash
# Check the queue implementation to understand pool behavior
echo "=== Checking pool queue implementation ==="
cat txpool/src/pool/queue.rs | head -100

Length of output: 3481


Update line 18 documentation to reflect the actual default value of 40,960,000.

Line 18 comment states "default to 4096" but the actual default on line 62 is 40,960,000. This documentation must be corrected.

The review comment's concerns about memory exhaustion and performance are somewhat overstated—the pool has a secondary memory-based limit that defaults to half of free system memory, which provides a safeguard. However, the documentation discrepancy is a definite issue requiring correction.

Update line 18 to:

/// Maximal number of transactions in the pool. default to 40960000

If the 10,000x increase is intentional, consider adding a comment explaining the rationale, especially if this supports specific use cases like VM2 benchmarking.

🤖 Prompt for AI Agents
In config/src/txpool_config.rs around lines 18 and 61-63, the doc comment on
line 18 incorrectly states the default is 4096 while the code returns
40,960,000; update the doc comment to read that the maximal number of
transactions defaults to 40960000 and, if the larger default is intentional,
append a brief rationale (e.g., VM2 benchmarking or specific workload support)
so readers understand why the value is 10,000x higher than the previous doc.

Comment on lines +82 to +85
let next_seq = statedb2
.get_account_resource(*s.address())?
.sequence_number();
next_seq_map.entry(*s.address()).or_insert(next_seq);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Compile fix: AccountInfo likely has field address, not method address().
Dereferencing s.address() will not compile; use the field.

-            .get_account_resource(*s.address())?
+            .get_account_resource(s.address)?
             .sequence_number();
-        next_seq_map.entry(*s.address()).or_insert(next_seq);
+        next_seq_map.entry(s.address).or_insert(next_seq);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let next_seq = statedb2
.get_account_resource(*s.address())?
.sequence_number();
next_seq_map.entry(*s.address()).or_insert(next_seq);
let next_seq = statedb2
.get_account_resource(s.address)?
.sequence_number();
next_seq_map.entry(s.address).or_insert(next_seq);
🤖 Prompt for AI Agents
In sync/tests/full_build_and_execute_test.rs around lines 82 to 85, replace the
incorrect method call and dereference (*s.address()) with the struct field
access (s.address); if Address isn't Copy, use s.address.clone() or otherwise
supply the correct owned/borrowed form to next_seq_map.entry, so the code
compiles with the AccountInfo.address field.

/// Error for delta application. Can be used by delta partial functions
/// to return descriptive error messages and an appropriate error code.
fn abort_error(message: impl ToString, code: u64) -> PartialVMError {
info!("jacktest: abort error: {:?}", message.to_string());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove debug prefix from production code.

The "jacktest:" prefix indicates this is temporary debugging code that should not be merged to production. This will clutter logs with test-prefixed messages.

If abort error logging is intended, apply this diff to clean it up:

-    info!("jacktest: abort error: {:?}", message.to_string());
+    warn!("Aggregator abort error: {}", message.to_string());

Otherwise, remove the log statement entirely:

-    info!("jacktest: abort error: {:?}", message.to_string());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
info!("jacktest: abort error: {:?}", message.to_string());
warn!("Aggregator abort error: {}", message.to_string());
Suggested change
info!("jacktest: abort error: {:?}", message.to_string());
🤖 Prompt for AI Agents
In vm2/aggregator/src/aggregator_v1_extension.rs around line 372, the log call
uses a debug/test prefix "jacktest:" that must be removed for production; either
delete the entire info! call if the abort error should not be logged, or keep
the info! call but remove the "jacktest:" prefix and reword it to something like
"abort error:" (preserving the {:?} message formatting) so production logs are
clean and descriptive.


pub fn convert_prologue_runtime_error(error: VMError) -> Result<(), VMStatus> {
let status = error.into_vm_status();
info!("jacktest: convert_prologue_runtime_error status: {}", status);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove temporary debug log before merge.

This info! log with the "jacktest" prefix appears to be temporary debugging scaffolding. Debug logs should be removed or converted to debug! level before merging to a production branch.

Apply this diff to remove the temporary log:

-    info!("jacktest: convert_prologue_runtime_error status: {}", status);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
info!("jacktest: convert_prologue_runtime_error status: {}", status);
🤖 Prompt for AI Agents
In vm2/vm-runtime/src/errors.rs around line 87, remove the temporary info! log
that reads "jacktest: convert_prologue_runtime_error status: {}" (or
alternatively convert it to debug! if you want to keep the message for local
debugging); update the file by deleting that info! line (or replacing info! with
debug!) so no temporary info-level debug log remains in the production branch.

// function calls. We know the feature is active when more structs then just strings are
// allowed.
let are_struct_constructors_enabled = allowed_structs.len() > 1;
info!("jacktest: are_struct_constructors_enabled: {}", are_struct_constructors_enabled);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove or clean up the test/debug log statement.

The log statement contains a "jacktest:" prefix, indicating this is debug or test code. Test artifacts should be removed before merging to production.

Apply this diff to remove the debug log:

-            info!("jacktest: are_struct_constructors_enabled: {}", are_struct_constructors_enabled);

Alternatively, if this logging is intentional for production diagnostics, remove the "jacktest:" prefix and provide a more descriptive message:

-            info!("jacktest: are_struct_constructors_enabled: {}", are_struct_constructors_enabled);
+            info!("Struct constructors enabled: {}", are_struct_constructors_enabled);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
info!("jacktest: are_struct_constructors_enabled: {}", are_struct_constructors_enabled);
Suggested change
info!("jacktest: are_struct_constructors_enabled: {}", are_struct_constructors_enabled);
info!("Struct constructors enabled: {}", are_struct_constructors_enabled);
🤖 Prompt for AI Agents
In vm2/vm-runtime/src/verifier/transaction_arg_validation.rs around line 379,
remove the debug/test log statement or replace it with a production-safe
message; either delete the line containing info!("jacktest:
are_struct_constructors_enabled: {}", are_struct_constructors_enabled); or
change it to a clear, descriptive log without the "jacktest:" prefix (e.g.,
info!("are_struct_constructors_enabled = {}", are_struct_constructors_enabled);)
so no test artifact remains in production code.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
sync/tests/full_build_and_execute_test.rs (4)

3-3: Compile fix: remove non-existent anyhow::Ok.

anyhow::Ok isn't a thing; Ok is from the std prelude.

-use anyhow::{bail, format_err, Ok, Result};
+use anyhow::{bail, format_err, Result};

82-86: Compile fix: use AccountInfo.address field, not address().

The struct exposes a field (and later lines use it already).

-            .get_account_resource(*s.address())?
+            .get_account_resource(s.address)?
             .sequence_number();
-        next_seq_map.entry(*s.address()).or_insert(next_seq);
+        next_seq_map.entry(s.address).or_insert(next_seq);

169-177: Ensure the intended genesis_config is used when loading NodeConfig.

You're setting opt.genesis_config but loading with global_opt, which ignores it.

Option B — simplest (preferred here):

-    let node_config = Arc::new(NodeConfig::load_with_opt(&global_opt)?);
+    let node_config = Arc::new(NodeConfig::load_with_opt(&opt)?);

(Alternatively, make global_opt mutable and set its genesis_config before load.)

Also applies to: 179-179


247-255: Bound txpool-drain waits with deadlines to prevent hangs.

Both loops can hang indefinitely; add a deadline check.

-        loop {
+        let deadline = std::time::Instant::now() + std::time::Duration::from_secs(180);
+        loop {
             let transactions = txpool
                 .inner
                 .get_pending(100, config.net().time_service().now_secs())?;
             if transactions.is_empty() {
                 break;
             }
+            if std::time::Instant::now() >= deadline {
+                bail!("timed out waiting for txpool to empty (first batch)");
+            }
             tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
         }
@@
-        loop {
+        let deadline = std::time::Instant::now() + std::time::Duration::from_secs(180);
+        loop {
             let transactions = txpool
                 .inner
                 .get_pending(100, config.net().time_service().now_secs())?;
             if transactions.is_empty() {
                 break;
             }
+            if std::time::Instant::now() >= deadline {
+                bail!("timed out waiting for txpool to empty (second batch)");
+            }
             tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
         }

Also applies to: 282-290

🧹 Nitpick comments (6)
sync/tests/full_build_and_execute_test.rs (6)

181-183: Prefer ? over unwrap() in tests returning Result.

Surface errors instead of panicking.

-    let node = run_node_with_all_service(node_config.clone()).unwrap();
+    let node = run_node_with_all_service(node_config.clone())?;

28-28: Remove unused import PeerId.

Avoid warnings; keep imports minimal.

-use starcoin_vm2_vm_types::{state_view::StateReaderExt, PeerId, account_address::AccountAddress};
+use starcoin_vm2_vm_types::{state_view::StateReaderExt, account_address::AccountAddress};

31-37: Nit: parameter name typo.

Use account_number for clarity.

-async fn create_account(
-    accoun_number: u32,
+async fn create_account(
+    account_number: u32,
@@
-    for _ in 0..accoun_number {
+    for _ in 0..account_number {

132-139: Nit: fix serivice typo in param.

Keeps code tidy and searchable.

-async fn get_current_header(
-    chain_reader_serivice: ServiceRef<ChainReaderService>,
+async fn get_current_header(
+    chain_reader_service: ServiceRef<ChainReaderService>,
 ) -> Result<BlockHeader> {
-    let current_header = match chain_reader_serivice
+    let current_header = match chain_reader_service

41-43: Nit: fix message typos (“Unexpected”).

Minor text polish.

-            _ => bail!("Unexpect response type."),
+            _ => bail!("Unexpected response type."),
@@
-        _ => bail!("Unexpect response type."),
+        _ => bail!("Unexpected response type."),
@@
-            AccountResponse::AccountInfo(_) => (),
-            _ => bail!("Unexpect response type."),
+            AccountResponse::AccountInfo(_) => (),
+            _ => bail!("Unexpected response type."),
@@
-            AccountResponse::SignedTxn(signed_transction) => *signed_transction,
-            _ => bail!("Unexpect response type."),
+            AccountResponse::SignedTxn(signed_transction) => *signed_transction,
+            _ => bail!("Unexpected response type."),
@@
-        _ => bail!("Unexpect response type."),
+        _ => bail!("Unexpected response type."),

Also applies to: 57-59, 113-115, 123-125, 139-141


152-154: Gate the benchmark-style test in CI.

Mark as ignored to avoid long runs by default; run with cargo test -- --ignored when needed.

-// #[ignore = "This is a benchmark test, not a unit test"]
+#[ignore]
 #[test]
 fn test_full_build_and_execute_in_custom_network() -> Result<()> {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f60b61f and eb71de1.

📒 Files selected for processing (1)
  • sync/tests/full_build_and_execute_test.rs (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-14T15:08:48.415Z
Learnt from: jackzhhuang
PR: starcoinorg/starcoin#4648
File: miner/tests/miner_test.rs:87-93
Timestamp: 2025-09-14T15:08:48.415Z
Learning: In Starcoin test files (like miner/tests/miner_test.rs), jackzhhuang prefers to use std::thread::sleep for intentional blocking delays in test scenarios, even within actor event handlers, rather than using non-blocking ctx.run_later alternatives.

Applied to files:

  • sync/tests/full_build_and_execute_test.rs
🧬 Code graph analysis (1)
sync/tests/full_build_and_execute_test.rs (6)
config/src/lib.rs (6)
  • temp_dir (110-113)
  • default (153-155)
  • default (281-283)
  • path (263-265)
  • load_with_opt (294-319)
  • load_with_opt (549-552)
test-helper/src/node.rs (1)
  • run_node_with_all_service (24-28)
vm2/account/api/src/service.rs (2)
  • create_account (17-17)
  • create_account (93-100)
node/src/lib.rs (19)
  • config (128-132)
  • storage2 (140-144)
  • new (78-92)
  • registry (120-122)
  • registry (129-130)
  • registry (135-136)
  • registry (141-142)
  • registry (147-148)
  • registry (153-153)
  • registry (157-158)
  • registry (163-163)
  • registry (167-167)
  • registry (171-171)
  • registry (190-191)
  • registry (196-197)
  • registry (203-203)
  • registry (214-214)
  • registry (215-215)
  • txpool (189-193)
vm2/vm-runtime/src/starcoin_vm.rs (3)
  • new (87-110)
  • new (113-136)
  • set_concurrency_level_once (1405-1410)
txpool/src/tx_pool_service_impl.rs (1)
  • new (41-83)
🔇 Additional comments (1)
sync/tests/full_build_and_execute_test.rs (1)

105-115: Code is correct; original review comment is inaccurate.

The AccountRequest::UnlockAccount handler in account/service/src/service.rs (lines 149–154) definitively returns AccountResponse::AccountInfo(Box::new(account_info)). The test code's match on AccountResponse::AccountInfo(_) is correct and will properly handle the response. While UnlockAccountResponse exists as an enum variant, it is not what UnlockAccount returns.

Likely an incorrect or invalid review comment.

Comment on lines +162 to +165
let path = temp_dir();
opt.base_data_dir = Some(path.clone());
opt.genesis_config = Some("halley".to_string());

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use a unique temp subdirectory and clean it up to avoid cross-test pollution.

Writing directly to the OS temp root risks collisions and leaves artifacts.

-    let path = temp_dir();
-    opt.base_data_dir = Some(path.clone());
+    let path = temp_dir().join(format!("starcoin_vm2_bench_{}", std::process::id()));
+    std::fs::create_dir_all(&path)?;
+    opt.base_data_dir = Some(path.clone());
@@
-    // std::fs::remove_dir_all(path)?;
+    std::fs::remove_dir_all(path)?;

Also applies to: 296-298

🤖 Prompt for AI Agents
In sync/tests/full_build_and_execute_test.rs around lines 162-165 (and similarly
at 296-298) the test writes directly into the OS temp root which can cause
collisions and leftover artifacts; change it to create a unique temporary
subdirectory (e.g., use tempfile::tempdir() or create a uniquely named subdir
under std::env::temp_dir()), set opt.base_data_dir to that tempdir's path, and
ensure the tempdir is cleaned up when the test finishes (keep the TempDir value
in scope for the test or explicitly remove the directory at the end) so each
test gets an isolated, auto-deleted workspace.

Comment on lines 201 to 216
loop {
// generate_block.notify(DeterminedDagBlock)?;
let current_header = get_current_header(chain_reader_service.clone()).await?;
let default_account_balance = match get_balance(default_account.address, storage1.clone(), storage2.clone(), current_header.id()).await {
std::result::Result::Ok(balance) => balance,
Err(e) => {
info!("get balance error: {} and waiting for the token initialization", e);
tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
continue;
}
};
if default_account_balance > account_count as u128 * initial_balance + initial_gas_fee { // get enough token to pay for gas
break;
}
tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add a timeout to avoid hanging while waiting for initial balance.

This loop can run forever if initialization fails; bail after a deadline.

-        loop {
+        let deadline = std::time::Instant::now() + std::time::Duration::from_secs(300);
+        loop {
             // generate_block.notify(DeterminedDagBlock)?;
             let current_header = get_current_header(chain_reader_service.clone()).await?;
             let default_account_balance = match get_balance(default_account.address, storage1.clone(), storage2.clone(), current_header.id()).await {
                 std::result::Result::Ok(balance) => balance,
                 Err(e) => {
                     info!("get balance error: {} and waiting for the token initialization", e);
                     tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
                     continue;
                 }
             };
             if default_account_balance > account_count as u128 * initial_balance + initial_gas_fee { // get enough token to pay for gas
                 break;
             }
+            if std::time::Instant::now() >= deadline {
+                bail!("timed out waiting for default account balance initialization");
+            }
             tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
loop {
// generate_block.notify(DeterminedDagBlock)?;
let current_header = get_current_header(chain_reader_service.clone()).await?;
let default_account_balance = match get_balance(default_account.address, storage1.clone(), storage2.clone(), current_header.id()).await {
std::result::Result::Ok(balance) => balance,
Err(e) => {
info!("get balance error: {} and waiting for the token initialization", e);
tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
continue;
}
};
if default_account_balance > account_count as u128 * initial_balance + initial_gas_fee { // get enough token to pay for gas
break;
}
tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
}
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(300);
loop {
// generate_block.notify(DeterminedDagBlock)?;
let current_header = get_current_header(chain_reader_service.clone()).await?;
let default_account_balance = match get_balance(default_account.address, storage1.clone(), storage2.clone(), current_header.id()).await {
std::result::Result::Ok(balance) => balance,
Err(e) => {
info!("get balance error: {} and waiting for the token initialization", e);
tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
continue;
}
};
if default_account_balance > account_count as u128 * initial_balance + initial_gas_fee { // get enough token to pay for gas
break;
}
if std::time::Instant::now() >= deadline {
bail!("timed out waiting for default account balance initialization");
}
tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
}
🤖 Prompt for AI Agents
In sync/tests/full_build_and_execute_test.rs around lines 201 to 216, the loop
that polls get_balance can hang indefinitely; add a timeout/deadline check
(e.g., record start time and compare elapsed or use tokio::time::timeout) and
abort the wait when the deadline is exceeded, returning an Err (or failing the
test) with a descriptive message. Keep the existing retry/sleep behavior but
bail out after the configured duration (e.g., 30s or test-configurable) so the
test does not hang forever when initialization fails.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
txpool/src/tx_pool_service_impl.rs (1)

397-409: Remove or restrict the bypass_vm1_limit parameter from public API surfaces.

The visibility change exposes a security issue: external callers can now bypass VM1 abuse prevention limits by setting bypass_vm1_limit=true. The parameter should not be user-controllable—it should be computed automatically based on transaction type (as the EventHandler correctly does in txpool/src/lib.rs line 222-226).

The bypass is accessible through two paths:

  1. Direct calls to the now-public import_txns() method
  2. TxPoolSyncService::add_txns_multi_signed() which passes the parameter through

Recommended fixes:

  • Keep import_txns() as pub(crate), OR
  • Remove bypass_vm1_limit from TxPoolSyncService::add_txns_multi_signed() and compute it internally from transaction type, OR
  • Add authorization checks to restrict who can set bypass_vm1_limit=true (e.g., admin-only)
🧹 Nitpick comments (1)
txpool/src/tx_pool_service_impl.rs (1)

420-439: Exposing pending transaction retrieval looks reasonable.

This read-only method supports the new batch RPC endpoints. The visibility change appears appropriate for the intended use case.

Consider documenting the performance implications of calling this with large max_len values, especially for external callers who might not be aware of the pool size.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b963464 and 87a8313.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • sync/Cargo.toml (2 hunks)
  • sync/tests/full_build_and_execute_test.rs (1 hunks)
  • txpool/src/tx_pool_service_impl.rs (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-01T03:56:58.362Z
Learnt from: welbon
PR: starcoinorg/starcoin#4633
File: vm/vm-runtime/Cargo.toml:48-48
Timestamp: 2025-09-01T03:56:58.362Z
Learning: In the Starcoin codebase, vm1 (starcoin-vm1-vm-runtime) may need to expose the same feature flags as vm2 to satisfy cross-version compatibility requirements when downstream projects like genesis depend on features from both versions. Empty feature declarations like `move-unit-test = []` may be intentionally added for compilation compatibility rather than to activate specific functionality.

Applied to files:

  • sync/Cargo.toml
📚 Learning: 2025-09-14T15:08:48.415Z
Learnt from: jackzhhuang
PR: starcoinorg/starcoin#4648
File: miner/tests/miner_test.rs:87-93
Timestamp: 2025-09-14T15:08:48.415Z
Learning: In Starcoin test files (like miner/tests/miner_test.rs), jackzhhuang prefers to use std::thread::sleep for intentional blocking delays in test scenarios, even within actor event handlers, rather than using non-blocking ctx.run_later alternatives.

Applied to files:

  • sync/tests/full_build_and_execute_test.rs
🧬 Code graph analysis (1)
sync/tests/full_build_and_execute_test.rs (5)
config/src/lib.rs (6)
  • temp_dir (110-113)
  • default (153-155)
  • default (281-283)
  • path (263-265)
  • load_with_opt (294-319)
  • load_with_opt (549-552)
test-helper/src/node.rs (1)
  • run_node_with_all_service (24-28)
vm2/account/api/src/service.rs (2)
  • create_account (17-17)
  • create_account (93-100)
node/src/lib.rs (19)
  • config (128-132)
  • storage2 (140-144)
  • new (78-92)
  • registry (120-122)
  • registry (129-130)
  • registry (135-136)
  • registry (141-142)
  • registry (147-148)
  • registry (153-153)
  • registry (157-158)
  • registry (163-163)
  • registry (167-167)
  • registry (171-171)
  • registry (190-191)
  • registry (196-197)
  • registry (203-203)
  • registry (214-214)
  • registry (215-215)
  • genesis (146-150)
vm2/vm-runtime/src/starcoin_vm.rs (3)
  • new (87-110)
  • new (113-136)
  • set_concurrency_level_once (1405-1410)
🔇 Additional comments (6)
sync/tests/full_build_and_execute_test.rs (5)

140-151: LGTM!

The helper function correctly retrieves the current chain header using the service request/response pattern.


153-158: LGTM!

The balance retrieval logic correctly constructs the state view and queries the account balance.


273-273: Verify intentional placement of concurrency level setting.

Setting the VM concurrency level between the two transfer batches appears intentional for benchmarking (measuring with/without parallelism). If this is deliberate, consider adding a comment explaining the benchmark design.


319-339: LGTM!

The TransactionExecutionResult enum cleanly models transaction states with timestamps and provides a clear Debug implementation.


411-441: LGTM!

The event handlers correctly track transaction state transitions across DAG block events and txpool status updates, with appropriate error logging.

txpool/src/tx_pool_service_impl.rs (1)

561-574: No evidence of misuse; review comment addresses speculative future risks.

All current usages of get_pool_client() follow the recommended pattern: each operation creates a fresh PoolClient bound to the current chain state. The method is called within public wrapper methods (verify_transaction, get_pending), but no direct external calls to get_pool_client() were found in the codebase. External callers invoking these public methods will receive a fresh client, maintaining state consistency as intended. The concerns raised in the review comment are speculative about potential future misuse rather than addressing observable issues in the current codebase. Verify whether this public method exposure is new to this PR and, if so, whether documentation should be added to clarify the intended external API contract.

hex = { workspace = true }
starcoin-miner = { workspace = true }
starcoin-account-api = { workspace = true }
starcoin-vm2-account-api = { workspace = true }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Approve additions of VM2-related dev-dependencies.

The new dependencies in lines 62 and 75-80 support the PR's VM2 benchmarking and batch operation objectives. Placement in [dev-dependencies] is appropriate, and the use of { workspace = true } maintains consistency with other dependencies.

However, please verify the following:

  1. Actual usage: Confirm that all added dependencies (starcoin-vm2-account-api, starcoin-account-service, starcoin-vm2-account-service, starcoin-vm2-statedb, starcoin-vm2-vm-runtime, starcoin-rpc-client, chrono) are actually referenced in sync's test or benchmark code. Adding unused dependencies increases build time and can mask dependency resolution issues.

  2. Circular dependencies: Verify that none of these dependencies depend on starcoin-sync. If any do, this introduces a circular dependency, which can cause build failures or versioning conflicts.

  3. Runtime vs. dev classification: Confirm that these belong in [dev-dependencies] and not in [dependencies]. For example, if starcoin-sync uses starcoin-account-service or starcoin-rpc-client at runtime (not just for tests), they should be promoted to main dependencies.

Run the following script to help verify:

Also applies to: 75-80


🏁 Script executed:

#!/bin/bash
# Description: Verify that added dev-dependencies are actually used in sync tests/benchmarks
# and check for circular dependency patterns.

# Find all test/benchmark files in sync crate
echo "=== Test and benchmark files in sync crate ==="
find . -path ./target -prune -o -name "*.rs" -print | xargs grep -l "mod test\|#\[test\]\|#\[bench\]" | grep "sync"

# Check if any of the added dependencies mention starcoin-sync in their Cargo.toml
echo ""
echo "=== Checking for circular dependencies (added deps that depend on starcoin-sync) ==="
for dep in "starcoin-vm2-account-api" "starcoin-account-service" "starcoin-vm2-account-service" "starcoin-vm2-statedb" "starcoin-vm2-vm-runtime" "starcoin-rpc-client"; do
  echo "Checking $dep..."
  fd -e "Cargo.toml" | xargs grep -l "^name = \"$dep\"" | xargs grep "starcoin-sync" && echo "  Found: $dep depends on starcoin-sync" || echo "  OK: No circular dependency detected"
done

# Show which added dependencies are actually used in sync source
echo ""
echo "=== Usage of added dependencies in sync/src ==="
for dep in "vm2_account_api" "account_service" "vm2_account_service" "vm2_statedb" "vm2_vm_runtime" "rpc_client"; do
  count=$(find . -path ./target -prune -o -path "*/sync/src/*.rs" -print | xargs grep -c "$dep" 2>/dev/null || echo "0")
  echo "$dep: $count occurrences"
done

Length of output: 8241



🏁 Script executed:

#!/bin/bash
# Check if unused dependencies are actually used in sync tests or inline test code

echo "=== Checking sync/tests directory for unused dependencies ==="
for dep in "vm2_account_api" "account_service" "vm2_account_service" "vm2_statedb" "vm2_vm_runtime"; do
  echo ""
  echo "Searching for $dep in sync/tests..."
  find ./sync/tests -name "*.rs" -exec grep -l "$dep" {} \; 2>/dev/null || echo "  Not found in sync/tests"
done

echo ""
echo ""
echo "=== Checking inline test modules for unused dependencies ==="
for dep in "vm2_account_api" "account_service" "vm2_account_service" "vm2_statedb" "vm2_vm_runtime"; do
  echo ""
  echo "Searching for $dep in #[cfg(test)] blocks..."
  rg -n "#\[cfg\(test\)\]" -A 200 --type rs --path-separator / | grep -B5 -A5 "$dep" 2>/dev/null || true
done

echo ""
echo ""
echo "=== Verify rpc_client is used at runtime (not just in tests) ==="
echo "Checking rpc_client in sync.rs (8 occurrences found)..."
grep -n "rpc_client" ./sync/src/sync.rs | head -10

echo ""
echo "Checking rpc_client in announcement/mod.rs (3 occurrences found)..."
grep -n "rpc_client" ./sync/src/announcement/mod.rs | head -5

echo ""
echo "Checking if these are in test blocks or runtime code..."
grep -B5 -A5 "rpc_client" ./sync/src/verified_rpc_client.rs | head -30

Length of output: 1344



🏁 Script executed:

#!/bin/bash
# Check if unused dependencies are used in sync tests or inline test code

echo "=== Checking for five unused dependencies in sync/tests ==="
for dep in "vm2_account_api" "account_service" "vm2_account_service" "vm2_statedb" "vm2_vm_runtime"; do
  echo "Searching for $dep in sync/tests..."
  count=$(find ./sync/tests -name "*.rs" 2>/dev/null | xargs grep -c "$dep" 2>/dev/null || echo "0")
  echo "  Found in tests: $count occurrences"
done

echo ""
echo "=== Checking for inline test usage ==="
for dep in "vm2_account_api" "account_service" "vm2_account_service" "vm2_statedb" "vm2_vm_runtime"; do
  echo "Searching for $dep in #[cfg(test)] blocks..."
  count=$(find ./sync/src -name "*.rs" -exec grep -l "#\[cfg(test)\]" {} \; 2>/dev/null | xargs grep -c "$dep" 2>/dev/null || echo "0")
  echo "  Found in test blocks: $count occurrences"
done

echo ""
echo "=== Verify rpc_client context (runtime import vs test-only) ==="
echo "File: sync/src/sync.rs"
grep -n "rpc_client" ./sync/src/sync.rs 2>/dev/null | head -5

echo ""
echo "File: sync/src/verified_rpc_client.rs"
head -30 ./sync/src/verified_rpc_client.rs 2>/dev/null | grep -n "rpc_client" || echo "Checking context..."
head -50 ./sync/src/verified_rpc_client.rs 2>/dev/null

echo ""
echo "=== Check chrono usage (line 80 addition) ==="
count=$(find ./sync -name "*.rs" | xargs grep -c "chrono" 2>/dev/null || echo "0")
echo "chrono occurrences in sync: $count"
find ./sync -name "*.rs" | xargs grep -n "chrono" 2>/dev/null | head -5

Length of output: 7033


Move starcoin-rpc-client from [dev-dependencies] to [dependencies].

The verification reveals that starcoin-rpc-client is used at runtime in sync/src/sync.rs (imports and calls like fetch_blocks() at lines 241, 271, 318, 497), but is currently placed in [dev-dependencies] at line 79. This is a critical misclassification—runtime dependencies must be in [dependencies].

The five other new dev-dependencies (starcoin-vm2-account-api, starcoin-account-service, starcoin-vm2-account-service, starcoin-vm2-statedb, starcoin-vm2-vm-runtime) are correctly placed in [dev-dependencies] as they are used only in sync/tests/full_build_and_execute_test.rs. Chrono is also appropriately dev-only. No circular dependencies detected.

🤖 Prompt for AI Agents
In sync/Cargo.toml around lines 62-62: the crate starcoin-rpc-client is
currently listed under [dev-dependencies] (around line 79) but is used at
runtime in sync/src/sync.rs; move the starcoin-rpc-client entry out of
[dev-dependencies] and add the same dependency specification under
[dependencies] (preserve any version or workspace = true attribute it currently
has), removing the dev-dependencies entry so the crate is available for normal
builds.

use std::io::{self, Write};

async fn create_account(
accoun_number: u32,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix typo in parameter name.

The parameter name accoun_number is missing a 't'.

Apply this diff:

-    accoun_number: u32,
+    account_number: u32,

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In sync/tests/full_build_and_execute_test.rs around line 40, the
struct/parameter name `accoun_number` has a typo and should be `account_number`;
rename the field to `account_number` and update all references in this file (and
any related test helpers or pattern matches) to use the corrected identifier so
the code compiles and tests refer to the consistent name.

Comment on lines 367 to 373
fn dump_results(&mut self) -> Result<()> {
let mut file = OpenOptions::new().write(true).create(true).truncate(true).open("/Users/jack/Documents/code/rust/flexidag/starcoin-jack/main/starcoin/sync/transaction_results.txt")?;
for (transaction, results) in &self.transaction_data {
writeln!(file, "transaction id: {}, results: {:?}", *transaction, results)?;
}
Ok(())
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Replace hardcoded absolute path with portable alternative.

The hardcoded path /Users/jack/Documents/... will fail on other machines, CI, and non-macOS systems.

Apply this diff to use a temporary file:

 fn dump_results(&mut self) -> Result<()> {
-    let mut file = OpenOptions::new().write(true).create(true).truncate(true).open("/Users/jack/Documents/code/rust/flexidag/starcoin-jack/main/starcoin/sync/transaction_results.txt")?;
+    let output_path = std::env::temp_dir().join("starcoin_transaction_results.txt");
+    let mut file = OpenOptions::new().write(true).create(true).truncate(true).open(output_path.clone())?;
+    info!("Transaction results written to: {:?}", output_path);
     for (transaction, results) in &self.transaction_data {
🤖 Prompt for AI Agents
In sync/tests/full_build_and_execute_test.rs around lines 367 to 373, the test
currently writes to a hardcoded absolute path which is non-portable; replace
that with a temporary file created at runtime (e.g., use std::env::temp_dir with
a unique filename or use the tempfile crate's NamedTempFile) and write the
results to that temp file instead; ensure the function opens/creates the temp
file via the portable API, writes the same content, and returns Ok(()) (or
returns the temp file path if callers need it) so the test no longer depends on
a machine-specific path.

Comment on lines +394 to +403
fn stopped(&mut self, ctx: &mut ServiceContext<Self>) -> Result<()> {
// ctx.unsubscribe::<NewHeadBlock>();
ctx.unsubscribe::<NewDagBlock>();
ctx.unsubscribe::<NewDagBlockFromPeer>();

if let Err(e) = self.dump_results() {
error!("failed to dump the reuslts: {:?}", e);
}
Ok(())
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix typo in error message.

Line 400 has a typo: "reuslts" should be "results".

Apply this diff:

-            error!("failed to dump the reuslts: {:?}", e);
+            error!("failed to dump the results: {:?}", e);
🤖 Prompt for AI Agents
In sync/tests/full_build_and_execute_test.rs around lines 394 to 403, the error
log message in stopped() contains a typo "reuslts"; change the string to
"results" so the processLogger/error call reads "failed to dump the results:
{:?}", keeping the existing error formatting and semantics intact.

Comment on lines +365 to 367
pub fn queue(&self) -> Arc<TxnQueue> {
self.queue.clone()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify access control at call sites and document usage constraints.

Exposing direct access to the internal transaction queue widens the API surface and could allow unintended manipulation if not carefully controlled. Ensure that all external callers have appropriate authorization checks and cannot bypass the intended transaction flow.

Run the following script to identify all external callers and verify proper access control:

Consider adding documentation to clarify:

  • Intended usage patterns for external callers
  • Any constraints or invariants that must be maintained
  • Whether direct queue manipulation is allowed or if this is read-only access

🏁 Script executed:

#!/bin/bash
# Description: Find all usages of the newly public queue() method to verify access control

# Search for calls to queue() method on Inner or TxPoolService
rg -nP --type=rust -C3 '\b(inner|tx_pool_service|txpool_service)\.queue\s*\(' -g '!**/test/**'

Length of output: 580



🏁 Script executed:

#!/bin/bash
# Check for any usages without the test exclusion to ensure nothing was missed
# Also look for direct queue() calls regardless of object name
rg -nP --type=rust '\.queue\s*\(' -C2

echo "=== Checking other visibility changes ==="
# Search for calls to the other three changed methods
rg -nP --type=rust '\.(import_txns|get_pending|get_pool_client)\s*\(' -C2 | head -50

Length of output: 3659


🏁 Script executed:

#!/bin/bash
# Verify the TxnQueue type and add_full_listener method to understand what's being exposed
# Also check if there are guard rails on queue access
cd txpool
fd -e rs -x grep -l "struct TxnQueue\|type TxnQueue\|trait.*TxnQueue" {} \;

Length of output: 29



🏁 Script executed:

#!/bin/bash
# Find TxnQueue type definition and understand what operations are publicly available
rg -nP --type=rust 'type\s+TxnQueue|struct\s+TxnQueue' -A 5 txpool/src/

echo "=== Check TxnQueue/TransactionQueue definition ==="
# Find the actual type alias or struct definition
rg -nP --type=rust 'TxnQueue\s*=' txpool/src/

echo "=== Look for add_full_listener method to understand what queue exposes ==="
rg -nP --type=rust 'fn add_full_listener' -B2 -A8

echo "=== Check if wrapper methods add validation before calling inner methods ==="
# Look at batch_import_transactions wrapper in lib.rs vs inner.import_txns
grep -nP -A10 'pub fn batch_import_transactions|pub fn handle_batch_import_transactions' txpool/src/lib.rs | head -40

Length of output: 2208



🏁 Script executed:

#!/bin/bash
# Get the complete implementation of wrapper methods to check for validation
echo "=== TxPoolService.batch_import_transactions (wrapper) ==="
sed -n '210,240p' txpool/src/lib.rs

echo ""
echo "=== TxPoolService.batch_get_pending (wrapper) ==="
sed -n '80,95p' txpool/src/lib.rs

echo ""
echo "=== Inner.import_txns (inner method being exposed) ==="
sed -n '397,415p' txpool/src/tx_pool_service_impl.rs

echo ""
echo "=== Inner.get_pending (inner method being exposed) ==="
sed -n '420,430p' txpool/src/tx_pool_service_impl.rs

echo ""
echo "=== Check what bypass_vm1_limit does ==="
rg -nP --type=rust 'bypass_vm1_limit' txpool/src/ -B2 -A2 | head -40

Length of output: 5126


Restrict public access to import_txns() or add guard logic—bypass_vm1_limit parameter must not be controllable without validation.

The visibility change exposes a security concern: import_txns() takes a bypass_vm1_limit parameter that allows bypassing VM1 transaction limits. The wrapper method in lib.rs:222–230 carefully guards this parameter, setting it to true only when all transactions are VM2. Making import_txns() public allows external callers to invoke it directly with bypass_vm1_limit=true for non-VM2 transactions, circumventing the intended validation logic.

The queue() method has minimal production usage (test-only), so its exposure is lower risk, but it still exposes implementation details without documented constraints.

Action required:

  • Either keep import_txns() as pub(crate) and route all external calls through the wrapper in lib.rs, or
  • Add guard logic directly in import_txns() to validate that bypass_vm1_limit is only set for VM2 transactions
  • Document any usage constraints for the newly public methods

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (12)
cmd/tx-factory/src/main.rs (3)

108-138: Infinite busy-wait remains unresolved from previous review.

The indefinite loop at lines 115-118 will hang the process if VM2 never provides a default account. This was flagged in the previous review and remains unaddressed.

Apply bounded retries with a configurable timeout:

-fn get_account_or_default2(
+fn get_account_or_default2(
     client: &RpcClient,
     account_address: Option<AccountAddress2>,
+    max_retries: u32,
 ) -> Result<AccountInfo2> {
     let account = match account_address {
         None => {
-            let mut default_account = client.account_default2()?;
-            while default_account.is_none() {
-                std::thread::sleep(Duration::from_millis(1000));
-                default_account = client.account_default2()?;
-            }
+            let mut default_account = None;
+            for attempt in 0..max_retries {
+                default_account = client.account_default2()?;
+                if default_account.is_some() {
+                    break;
+                }
+                if attempt < max_retries - 1 {
+                    std::thread::sleep(Duration::from_millis(1000));
+                }
+            }
+            if default_account.is_none() {
+                bail!("VM2 default account not found after {} retries", max_retries);
+            }
             
             // let addr = default_account.clone().unwrap().address;

205-209: Unconditional VM2 account fetch still blocks VM1 runs.

Line 206 fetches the VM2 account regardless of vm_version, causing VM1-only runs to hang if no VM2 account exists. This was flagged in the previous review and remains unfixed.

Make VM2 account fetching conditional:

     let account = get_account_or_default(&client, account_address).unwrap();
-    let account2 = get_account_or_default2(&client, account_address2).unwrap();
+    let account2 = if vm_version == 2 {
+        Some(get_account_or_default2(&client, account_address2, 30).unwrap())
+    } else {
+        None
+    };
 
     let receiver_address = opts.receiver_address.unwrap_or_else(association_address);
-    let receiver_address2 = opts.receiver_address2.unwrap_or_else(association_address2);
+    let receiver_address2 = if vm_version == 2 {
+        Some(opts.receiver_address2.unwrap_or_else(association_address2))
+    } else {
+        None
+    };

212-228: TxnMocker still requires VM2 args for VM1 paths.

Both MockTxnGenerator and TxnMocker constructors require VM2 accounts and addresses even when vm_version == 1. This tight coupling was flagged in the previous review.

Refactor to accept Option types or provide separate constructors:

// In txn_generator.rs
let txn_generator = if vm_version == 2 {
    MockTxnGenerator::new_dual(
        net.chain_id(),
        account.clone(),
        receiver_address,
        account2.unwrap().clone(),
        receiver_address2.unwrap(),
    )
} else {
    MockTxnGenerator::new_vm1(
        net.chain_id(),
        account.clone(),
        receiver_address,
    )
};

// Similar adjustment for TxnMocker::new
sync/tests/full_build_and_execute_test.rs (9)

3-3: Fix invalid import: anyhow::Ok does not exist.

The import statement includes Ok from anyhow, but Ok is provided by the Rust prelude and cannot be imported from anyhow. This will cause a compilation error.

Apply this diff:

-use anyhow::{bail, format_err, Ok, Result};
+use anyhow::{bail, format_err, Result};

47-47: Fix typo in parameter name.

The parameter accoun_number is missing a 't' and should be account_number.

Apply this diff:

-    accoun_number: u32,
+    account_number: u32,

97-100: Compile fix: AccountInfo has field address, not method address().

Lines 97-100 attempt to call s.address() as a method and dereference the result, but AccountInfo has an address field (as evidenced by line 105 which uses field access). This will cause a compilation error.

Apply this diff:

-        let next_seq = statedb2
-            .get_account_resource(*s.address())?
-            .sequence_number();
-        next_seq_map.entry(*s.address()).or_insert(next_seq);
+        let next_seq = statedb2
+            .get_account_resource(s.address)?
+            .sequence_number();
+        next_seq_map.entry(s.address).or_insert(next_seq);

182-183: Use a unique temp subdirectory to avoid test collisions.

Writing directly to the OS temp root (temp_dir()) can cause collisions with other tests and leaves artifacts. Create a unique subdirectory instead.

Apply this diff:

-    let path = temp_dir();
+    let path = temp_dir().join(format!("starcoin_vm2_bench_{}", std::process::id()));
+    std::fs::create_dir_all(&path)?;
     opt.base_data_dir = Some(path.clone());

Also applies to: 337-337


184-184: Use the intended genesis_config when loading NodeConfig.

You set opt.genesis_config on line 184, but then call NodeConfig::load_with_opt(&global_opt) on line 199, which ignores that configuration since global_opt doesn't have genesis_config set.

Apply this diff to set genesis_config on global_opt:

-    let global_opt = StarcoinOpt {
+    let mut global_opt = StarcoinOpt {
         net: Some(ChainNetworkID::Custom(CustomNetworkID::new(
             "my_chain".to_string(),
             ChainId::new(121),
         ))),
         base_data_dir: Some(path.clone()),
         ..Default::default()
     };
+    global_opt.genesis_config = opt.genesis_config.clone();

Also applies to: 189-199


223-249: Add a timeout to avoid hanging while waiting for initial balance.

This loop can run indefinitely if token initialization fails. Add a deadline and bail with a clear error message after a reasonable timeout.

Apply this diff:

         let chain_reader_service = registry.service_ref::<ChainReaderService>().await?;
+        let deadline = std::time::Instant::now() + std::time::Duration::from_secs(300);
         loop {
             // generate_block.notify(DeterminedDagBlock)?;
             let current_header = get_current_header(chain_reader_service.clone()).await?;
             let default_account_balance = match get_balance(
                 default_account.address,
                 storage1.clone(),
                 storage2.clone(),
                 current_header.id(),
             )
             .await
             {
                 std::result::Result::Ok(balance) => balance,
                 Err(e) => {
                     info!(
                         "get balance error: {} and waiting for the token initialization",
                         e
                     );
                     tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
                     continue;
                 }
             };
             if default_account_balance > account_count as u128 * initial_balance + initial_gas_fee {
                 // get enough token to pay for gas
                 break;
             }
+            if std::time::Instant::now() >= deadline {
+                bail!("timed out waiting for default account balance initialization");
+            }
             tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
         }

286-294: Add timeouts to txpool drain loops to prevent hanging.

Both loops that wait for the txpool to drain can spin indefinitely if transactions remain pending. Add a deadline to each loop.

Apply this diff for the first loop:

+        let deadline = std::time::Instant::now() + std::time::Duration::from_secs(120);
         loop {
             let transactions = txpool
                 .inner
                 .get_pending(100, config.net().time_service().now_secs())?;
             if transactions.is_empty() {
                 break;
             }
+            if std::time::Instant::now() >= deadline {
+                bail!("timed out waiting for txpool to drain (first round)");
+            }
             tokio::time::sleep(tokio::time::Duration::from_millis(3000)).await;
         }

Apply the same pattern to the second loop at lines 321-329.

Also applies to: 321-329


410-420: Replace hardcoded absolute path with portable alternative.

The hardcoded path /Users/jack/Documents/... will fail on other machines, CI systems, and non-macOS platforms.

Apply this diff:

     fn dump_results(&mut self) -> Result<()> {
-        let mut file = OpenOptions::new().write(true).create(true).truncate(true).open("/Users/jack/Documents/code/rust/flexidag/starcoin-jack/main/starcoin/sync/transaction_results.txt")?;
+        let output_path = std::env::temp_dir().join("starcoin_transaction_results.txt");
+        let mut file = OpenOptions::new().write(true).create(true).truncate(true).open(&output_path)?;
+        info!("Transaction results written to: {:?}", output_path);
         for (transaction, results) in &self.transaction_data {

446-446: Fix typo in error message.

The error message contains "reuslts" which should be "results".

Apply this diff:

-            error!("failed to dump the reuslts: {:?}", e);
+            error!("failed to dump the results: {:?}", e);
🧹 Nitpick comments (3)
kube/manifest/starcoin-halley.yaml (1)

32-33: The startup script uses block scalar syntax but remains a single unbroken bash line; add error handling and fix redundant cleanup.

The YAML |- block scalar is an improvement (addressing the prior review suggestion), but the script content is still a single continuous line without line breaks. Additionally, there are two issues:

  1. Redundant rm commands: rm -rf /sc-data/halley already removes the directory, making subsequent rm -rf /sc-data/halley/... calls unnecessary.
  2. Missing error handling: No set -e or error traps. If array indexing fails (e.g., $id out of bounds), or if NODE_KEYS parsing fails, the starcoin process starts with uninitialized/incorrect variables, potentially corrupting state.

Refactor to add line breaks and error handling:

         args:
-          - |
-            rm -rf /sc-data/halley; rm -rf /sc-data/halley/starcoin.ipc /sc-data/halley/starcoindb/db/starcoindb/LOCK /sc-data/halley/genesis_config.json; id=$(echo -e $POD_NAME | awk -F'-' '{print $2}'); IFS='; ' read -r -a node_keys <<< "$NODE_KEYS"; node_key=${node_keys[$id]}; node_key_flag=""; if [ -n "$node_key" ]; then node_key_flag="--node-key ${node_key}"; fi; miner_thread_flag=""; { case "$POD_NAME" in starcoin-0) miner_thread_flag="--miner-thread=1" ;; starcoin-1) miner_thread_flag="--miner-thread=2" ;; starcoin-2) miner_thread_flag="--miner-thread=3" ;; *) miner_thread_flag="" ;; esac; }; /starcoin/starcoin -n halley -d /sc-data --p2prpc-default-global-api-quota 9000/s --p2prpc-custom-user-api-quota get_header_by_hash=9000/s --p2prpc-custom-user-api-quota get_headers_by_hash=9000/s --p2prpc-custom-user-api-quota info=9000/s --p2prpc-custom-user-api-quota get_block_by_hash=9000/s --p2prpc-custom-user-api-quota get_block_ids=9000/s --p2prpc-custom-user-api-quota get_blocks=9000/s --jsonrpc-default-global-api-quota 9000/s --jsonrpc-custom-user-api-quota chain.get_headers_by_hash=9000/s --jsonrpc-custom-user-api-quota chain.get_header_by_hash=9000/s --jsonrpc-custom-user-api-quota chain.info=9000/s --jsonrpc-custom-user-api-quota chain.get_block_by_hash=9000/s --jsonrpc-custom-user-api-quota chain.get_block_ids=9000/s --jsonrpc-custom-user-api-quota chain.get_blocks=9000/s --min-peers-to-propagate 512 --max-peers-to-propagate 1024 --max-outgoing-peers 512 --max-incoming-peers 512 --txpool-tx-propagate-interval 1 --discover-local=true $node_key_flag $miner_thread_flag
+          - |-
+            set -e
+            
+            # Clean up data directory
+            rm -rf /sc-data/halley
+            
+            # Extract pod index and select node key
+            pod_id=$(echo "$POD_NAME" | awk -F'-' '{print $NF}')
+            IFS='; ' read -r -a node_keys <<< "$NODE_KEYS"
+            node_key="${node_keys[$pod_id]:-}"
+            
+            # Build node key flag
+            node_key_flag=""
+            [ -n "$node_key" ] && node_key_flag="--node-key $node_key"
+            
+            # Determine miner thread count from pod name
+            miner_thread_flag=""
+            case "$POD_NAME" in
+              starcoin-0) miner_thread_flag="--miner-thread=1" ;;
+              starcoin-1) miner_thread_flag="--miner-thread=2" ;;
+              starcoin-2) miner_thread_flag="--miner-thread=3" ;;
+            esac
+            
+            # Launch starcoin with VM2 benchmark flags
+            exec /starcoin/starcoin \
+              -n halley \
+              -d /sc-data \
+              --p2prpc-default-global-api-quota 9000/s \
+              --p2prpc-custom-user-api-quota get_header_by_hash=9000/s \
+              --p2prpc-custom-user-api-quota get_headers_by_hash=9000/s \
+              --p2prpc-custom-user-api-quota info=9000/s \
+              --p2prpc-custom-user-api-quota get_block_by_hash=9000/s \
+              --p2prpc-custom-user-api-quota get_block_ids=9000/s \
+              --p2prpc-custom-user-api-quota get_blocks=9000/s \
+              --jsonrpc-default-global-api-quota 9000/s \
+              --jsonrpc-custom-user-api-quota chain.get_headers_by_hash=9000/s \
+              --jsonrpc-custom-user-api-quota chain.get_header_by_hash=9000/s \
+              --jsonrpc-custom-user-api-quota chain.info=9000/s \
+              --jsonrpc-custom-user-api-quota chain.get_block_by_hash=9000/s \
+              --jsonrpc-custom-user-api-quota chain.get_block_ids=9000/s \
+              --jsonrpc-custom-user-api-quota chain.get_blocks=9000/s \
+              --min-peers-to-propagate 512 \
+              --max-peers-to-propagate 1024 \
+              --max-outgoing-peers 512 \
+              --max-incoming-peers 512 \
+              --txpool-tx-propagate-interval 1 \
+              --discover-local=true \
+              $node_key_flag \
+              $miner_thread_flag

Key improvements:

  • set -e ensures the script exits on errors
  • Removed redundant second rm call
  • Eliminated unnecessary echo -e flag
  • Use ${node_keys[$pod_id]:-} to safely handle array access
  • Split into logical sections with comments for maintenance
  • Line breaks improve readability significantly
  • exec replaces the shell process with starcoin, improving signal handling
cmd/tx-factory/src/main.rs (2)

120-128: Remove commented-out dead code.

The commented balance-checking logic has been disabled but left in place. Consider removing it to improve readability.

Also applies to: 152-160


187-189: Dispatch logic is sound; redundant panic can be simplified.

The VM version validation at lines 187-189 ensures vm_version is 1 or 2, making the panic at line 267 unreachable. You can simplify to an else without the condition check.

Apply this diff:

     let handle = if vm_version == 1 {
         start_vm1_pressure_test(
             tx_mocker,
             round_num,
             account_num,
             batch_size,
             interval,
             transfer_account_size,
             is_stress,
             stopping_signal,
         )
-    } else if vm_version == 2 {
+    } else {
         start_vm2_pressure_test(
             tx_mocker,
             round_num,
             account_num,
             batch_size,
             interval,
             transfer_account_size,
             is_stress,
             stopping_signal,
         )
-    } else {
-        panic!("vm version must be 1 or 2");
     };

Otherwise, the conditional dispatch correctly routes to VM1 or VM2 pressure tests.

Also applies to: 244-268

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87a8313 and e79c644.

📒 Files selected for processing (7)
  • cmd/tx-factory/src/main.rs (7 hunks)
  • config/src/txpool_config.rs (2 hunks)
  • kube/manifest/starcoin-halley.yaml (2 hunks)
  • sync/tests/full_build_and_execute_test.rs (1 hunks)
  • vm2/vm-runtime/src/errors.rs (1 hunks)
  • vm2/vm-runtime/src/starcoin_vm.rs (3 hunks)
  • vm2/vm-runtime/src/verifier/transaction_arg_validation.rs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • vm2/vm-runtime/src/errors.rs
  • config/src/txpool_config.rs
  • vm2/vm-runtime/src/verifier/transaction_arg_validation.rs
  • vm2/vm-runtime/src/starcoin_vm.rs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-14T15:08:48.415Z
Learnt from: jackzhhuang
PR: starcoinorg/starcoin#4648
File: miner/tests/miner_test.rs:87-93
Timestamp: 2025-09-14T15:08:48.415Z
Learning: In Starcoin test files (like miner/tests/miner_test.rs), jackzhhuang prefers to use std::thread::sleep for intentional blocking delays in test scenarios, even within actor event handlers, rather than using non-blocking ctx.run_later alternatives.

Applied to files:

  • sync/tests/full_build_and_execute_test.rs
🧬 Code graph analysis (2)
cmd/tx-factory/src/main.rs (2)
cmd/tx-factory/src/pressure_vm1.rs (1)
  • start_vm1_pressure_test (11-59)
cmd/tx-factory/src/pressure_vm2.rs (1)
  • start_vm2_pressure_test (13-61)
sync/tests/full_build_and_execute_test.rs (9)
chain/src/chain.rs (7)
  • info (1146-1152)
  • new (93-105)
  • current_header (1166-1168)
  • e (247-247)
  • e (1975-1975)
  • dag (259-261)
  • dag (1653-1655)
config/src/lib.rs (6)
  • temp_dir (110-113)
  • default (153-155)
  • default (281-283)
  • path (263-265)
  • load_with_opt (294-319)
  • load_with_opt (549-552)
test-helper/src/node.rs (1)
  • run_node_with_all_service (24-28)
vm2/account/api/src/service.rs (2)
  • create_account (17-17)
  • create_account (93-100)
node/src/lib.rs (20)
  • config (128-132)
  • storage2 (140-144)
  • new (78-92)
  • registry (120-122)
  • registry (129-130)
  • registry (135-136)
  • registry (141-142)
  • registry (147-148)
  • registry (153-153)
  • registry (157-158)
  • registry (163-163)
  • registry (167-167)
  • registry (171-171)
  • registry (190-191)
  • registry (196-197)
  • registry (203-203)
  • registry (214-214)
  • registry (215-215)
  • txpool (189-193)
  • genesis (146-150)
vm2/vm-runtime/src/starcoin_vm.rs (3)
  • new (87-110)
  • new (113-136)
  • set_concurrency_level_once (1408-1413)
txpool/src/tx_pool_service_impl.rs (1)
  • new (41-83)
vm2/rpc/server/src/account_rpc.rs (3)
  • get (120-128)
  • default (80-88)
  • create (100-108)
vm2/service/account/src/service.rs (6)
  • registry (222-222)
  • create (94-100)
  • ctx (65-66)
  • ctx (95-95)
  • ctx (96-96)
  • started (46-90)
🔇 Additional comments (2)
kube/manifest/starcoin-halley.yaml (1)

50-57: Verify the readiness probe removal does not break service traffic routing.

The readiness probe has been removed while liveness probe is retained. This is a significant operational change:

  • Readiness probes gate traffic routing; removing it may cause requests to route to nodes that aren't fully initialized.
  • Liveness probes only restart failed containers; they don't prevent traffic routing.

Please confirm this change is intentional and that downstream consumers of this StatefulSet are aware traffic may arrive before the node is fully ready.

cmd/tx-factory/src/main.rs (1)

4-21: LGTM: VM2 configuration fields and imports are appropriate.

The addition of VM2-specific imports and configuration fields properly extends the CLI to support dual-VM scenarios.

Also applies to: 42-106

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants