Repository: nervosnetwork/ckb (language: rust)

Issue:
How to determine if the node is in the "looking for assume-valid-target" stage?
## Feature Request

#### Is your feature request related to a problem? Please describe.
<!-- A clear and concise description of what the problem is. Ex. I have an issue when [...] -->
To better show the synchronization process, we will inform the user that they are in the early synchronization stage, which is 'looking for assume-valid-target'. So we need to know how to get that the ckb node is looking for assume-valid-target

#### Describe the solution you'd like
<!-- A clear and concise description of what you want to happen. Add any considered drawbacks. -->

Now we are using log matching for determination. But the log changes several times. So I want to know if there is a more accurate way to determine this.

#### Describe alternatives you've considered
<!-- A clear and concise description of any alternative solutions or features you've considered. -->

Could we use the `sync_status` to get the `best_known_block_number `, and check whether the `best_known_block_number` is less than the `assume-valid-target block` number?


Discussion / hints:
> Could we use the sync_status to get the best_known_block_number , and check whether the best_known_block_number is less than the assume-valid-target block number?

Technically possible.
However, strictly speaking, it's possible that when `best_known_block_number > assume-valid-target number`, `best_known_block_number` and `assume-valid-target` are not on the same chain (though this probability is extremely low)."

> > Could we use the sync_status to get the best_known_block_number , and check whether the best_known_block_number is less than the assume-valid-target block number?
> 
> Technically possible. However, strictly speaking, it's possible that when `best_known_block_number > assume-valid-target number`, `best_known_block_number` and `assume-valid-target` are not on the same chain (though this probability is extremely low)."

Are you planning to expose an interface to retrieve this state?
> > > Could we use the sync_status to get the best_known_block_number , and check whether the best_known_block_number is less than the assume-valid-target block number?
> > 
> > 
> > Technically possible. However, strictly speaking, it's possible that when `best_known_block_number > assume-valid-target number`, `best_known_block_number` and `assume-valid-target` are not on the same chain (though this probability is extremely low)."
> 
> Are you planning to expose an interface to retrieve this state?

Yes, we are currently planning to expose `assume_valid_target_found: bool` in the `sync_state` RPC interface to determine if CKB has found an assume-valid-target.


Fix patch:
diff --git a/ckb-bin/src/subcommand/run.rs b/ckb-bin/src/subcommand/run.rs
index 4c9178a889..827999fa5a 100644
--- a/ckb-bin/src/subcommand/run.rs
+++ b/ckb-bin/src/subcommand/run.rs
@@ -17,7 +17,7 @@ pub fn run(args: RunArgs, version: Version, async_handle: Handle) -> Result<(),
     info!("ckb version: {}", version);
     info!("run rpc server with {} threads", rpc_threads_num);
     let (mut rpc_handle, _rpc_stop_rx, _runtime) = new_global_runtime(Some(rpc_threads_num));
-    let mut launcher = Launcher::new(args, version, async_handle, rpc_handle.clone());
+    let launcher = Launcher::new(args, version, async_handle, rpc_handle.clone());
 
     let block_assembler_config = launcher.sanitize_block_assembler_config()?;
     let miner_enable = block_assembler_config.is_some();
@@ -45,8 +45,6 @@ pub fn run(args: RunArgs, version: Version, async_handle: Handle) -> Result<(),
         Some(shared.store().db().inner()),
     );
 
-    launcher.check_assume_valid_target(&shared);
-
     let chain_controller = launcher.start_chain_service(&shared, pack.take_proposal_table());
 
     launcher.start_block_filter(&shared);
diff --git a/rpc/README.md b/rpc/README.md
index ade3cd07d0..f2a27d8172 100644
--- a/rpc/README.md
+++ b/rpc/README.md
@@ -4138,12 +4138,16 @@ Response
   "id": 42,
   "jsonrpc": "2.0",
   "result": {
+    "assume_valid_target": "0x0000000000000000000000000000000000000000000000000000000000000000",
+    "assume_valid_target_reached": true,
     "best_known_block_number": "0x400",
     "best_known_block_timestamp": "0x5cd2b117",
     "fast_time": "0x3e8",
     "ibd": true,
     "inflight_blocks_count": "0x0",
     "low_time": "0x5dc",
+    "min_chain_work": "0x0",
+    "min_chain_work_reached": true,
     "normal_time": "0x4e2",
     "orphan_blocks_count": "0x0",
     "orphan_blocks_size": "0x0"
@@ -6833,6 +6837,10 @@ The overall chain synchronization state of this local node.
 
 `SyncState` is a JSON object with the following fields.
 
+* `assume_valid_target`: [`Byte32`](#type-byte32) - The assume_valid_target specified by ckb, if no assume_valid_target, this will be all zero.
+
+* `assume_valid_target_reached`: `boolean` - Is ckb reached the assume_valid_target? If no assume_valid_target, this will be true.
+
 * `best_known_block_number`: [`Uint64`](#type-uint64) - This is the best known block number observed by the local node from the P2P network.
 
     The best here means that the block leads a chain which has the best known accumulated difficulty.
@@ -6853,6 +6861,10 @@ The overall chain synchronization state of this local node.
 
 * `low_time`: [`Uint64`](#type-uint64) - The download scheduler's time analysis data, the low is the 9/10 of the cut-off point, unit ms
 
+* `min_chain_work`: [`Uint128`](#type-uint128) - This field acts as a security measure to ensure that a node only synchronizes with other nodes that have a significant amount of computational work invested in them, thereby preventing certain types of attacks and ensuring network integrity. Only the mainnet uses a hardcoded value for this field.
+
+* `min_chain_work_reached`: `boolean` - Is ckb reached the min_chain_work?
+
 * `normal_time`: [`Uint64`](#type-uint64) - The download scheduler's time analysis data, the normal is the 4/5 of the cut-off point, unit ms
 
 * `orphan_blocks_count`: [`Uint64`](#type-uint64) - Count of orphan blocks the local node has downloaded.
diff --git a/rpc/src/module/net.rs b/rpc/src/module/net.rs
index 658011f80e..0513b126ab 100644
--- a/rpc/src/module/net.rs
+++ b/rpc/src/module/net.rs
@@ -7,6 +7,7 @@ use ckb_jsonrpc_types::{
 use ckb_network::{extract_peer_id, multiaddr::Multiaddr, NetworkController};
 use ckb_sync::SyncShared;
 use ckb_systemtime::unix_time_as_millis;
+use ckb_types::prelude::Pack;
 use jsonrpc_core::Result;
 use jsonrpc_utils::rpc;
 use std::sync::Arc;
@@ -361,12 +362,16 @@ pub trait NetRpc {
     ///   "id": 42,
     ///   "jsonrpc": "2.0",
     ///   "result": {
+    ///     "assume_valid_target": "0x0000000000000000000000000000000000000000000000000000000000000000",
+    ///     "assume_valid_target_reached": true,
     ///     "best_known_block_number": "0x400",
     ///     "best_known_block_timestamp": "0x5cd2b117",
     ///     "fast_time": "0x3e8",
     ///     "ibd": true,
     ///     "inflight_blocks_count": "0x0",
     ///     "low_time": "0x5dc",
+    ///     "min_chain_work": "0x0",
+    ///     "min_chain_work_reached": true,
     ///     "normal_time": "0x4e2",
     ///     "orphan_blocks_count": "0x0",
     ///     "orphan_blocks_size": "0x0"
@@ -719,8 +724,22 @@ impl NetRpc for NetRpcImpl {
         let state = chain.shared().state();
         let (fast_time, normal_time, low_time) = state.read_inflight_blocks().division_point();
         let best_known = state.shared_best_header();
+        let min_chain_work = {
+            let mut min_chain_work_500k_u128: [u8; 16] = [0; 16];
+            min_chain_work_500k_u128
+                .copy_from_slice(&self.sync_shared.state().min_chain_work().to_le_bytes()[..16]);
+            u128::from_le_bytes(min_chain_work_500k_u128)
+        };
         let sync_state = SyncState {
             ibd: chain.is_initial_block_download(),
+            assume_valid_target_reached: state.assume_valid_target().is_none(),
+            assume_valid_target: state
+                .assume_valid_target_specified()
+                .unwrap_or_default()
+                .pack()
+                .into(),
+            min_chain_work: min_chain_work.into(),
+            min_chain_work_reached: state.min_chain_work_ready(),
             best_known_block_number: best_known.number().into(),
             best_known_block_timestamp: best_known.timestamp().into(),
             orphan_blocks_count: (state.orphan_pool().len() as u64).into(),
diff --git a/sync/src/types/mod.rs b/sync/src/types/mod.rs
index e634c5f838..a98431de21 100644
--- a/sync/src/types/mod.rs
+++ b/sync/src/types/mod.rs
@@ -1244,6 +1244,17 @@ impl SyncShared {
         Self::with_tmpdir::<PathBuf>(shared, sync_config, None, tx_relay_receiver)
     }
 
+    /// Check whether the data already exists in the database before starting
+    fn check_assume_valid_target_already_exists(sync_config: &SyncConfig, shared: &Shared) -> bool {
+        if let Some(ref target) = sync_config.assume_valid_target {
+            if shared.snapshot().block_exists(&target.pack()) {
+                info!("assume valid target is already in db, CKB will do full verification from now on");
+                return true;
+            }
+        }
+        false
+    }
+
     /// Generate a global sync state through configuration
     pub fn with_tmpdir<P>(
         shared: Shared,
@@ -1286,7 +1297,14 @@ impl SyncShared {
             inflight_blocks: RwLock::new(InflightBlocks::default()),
             pending_get_headers: RwLock::new(LruCache::new(GET_HEADERS_CACHE_SIZE)),
             tx_relay_receiver,
-            assume_valid_target: Mutex::new(sync_config.assume_valid_target),
+            assume_valid_target: Mutex::new({
+                if Self::check_assume_valid_target_already_exists(&sync_config, &shared) {
+                    None
+                } else {
+                    sync_config.assume_valid_target.clone()
+                }
+            }),
+            assume_valid_target_specified: sync_config.assume_valid_target,
             min_chain_work: sync_config.min_chain_work,
         };
 
@@ -1647,6 +1665,7 @@ pub struct SyncState {
     /* cached for sending bulk */
     tx_relay_receiver: Receiver<TxVerificationResult>,
     assume_valid_target: Mutex<Option<H256>>,
+    assume_valid_target_specified: Option<H256>,
     min_chain_work: U256,
 }
 
@@ -1655,6 +1674,10 @@ impl SyncState {
         self.assume_valid_target.lock()
     }
 
+    pub fn assume_valid_target_specified(&self) -> Option<H256> {
+        self.assume_valid_target_specified.clone()
+    }
+
     pub fn min_chain_work(&self) -> &U256 {
         &self.min_chain_work
     }
diff --git a/util/jsonrpc-types/src/net.rs b/util/jsonrpc-types/src/net.rs
index 502ee0f753..11de4b7038 100644
--- a/util/jsonrpc-types/src/net.rs
+++ b/util/jsonrpc-types/src/net.rs
@@ -1,4 +1,4 @@
-use crate::{BlockNumber, Byte32, Timestamp, Uint64};
+use crate::{BlockNumber, Byte32, Timestamp, Uint128, Uint64};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 
@@ -258,6 +258,18 @@ pub struct SyncState {
     /// During IBD, the local node only synchronizes the chain with one selected remote node and
     /// stops responding to most P2P requests.
     pub ibd: bool,
+    /// Is ckb reached the assume_valid_target? If no assume_valid_target, this will be true.
+    pub assume_valid_target_reached: bool,
+    /// The assume_valid_target specified by ckb, if no assume_valid_target, this will be all zero.
+    pub assume_valid_target: Byte32,
+    /// Is ckb reached the min_chain_work?
+    pub min_chain_work_reached: bool,
+    /// This field acts as a security measure to ensure that a node only
+    /// synchronizes with other nodes that have a significant amount of
+    /// computational work invested in them, thereby preventing certain types
+    /// of attacks and ensuring network integrity. Only the mainnet uses a
+    /// hardcoded value for this field.
+    pub min_chain_work: Uint128,
     /// This is the best known block number observed by the local node from the P2P network.
     ///
     /// The best here means that the block leads a chain which has the best known accumulated
diff --git a/util/launcher/src/lib.rs b/util/launcher/src/lib.rs
index 7b0c041036..b13bc0249e 100644
--- a/util/launcher/src/lib.rs
+++ b/util/launcher/src/lib.rs
@@ -26,7 +26,7 @@ use ckb_rpc::ServiceBuilder;
 use ckb_shared::Shared;
 
 use ckb_shared::shared_builder::{SharedBuilder, SharedPackage};
-use ckb_store::{ChainDB, ChainStore};
+use ckb_store::ChainDB;
 use ckb_sync::{BlockFilter, NetTimeProtocol, Relayer, SyncShared, Synchronizer};
 use ckb_tx_pool::service::TxVerificationResult;
 use ckb_types::prelude::*;
@@ -219,16 +219,6 @@ impl Launcher {
         Ok((shared, pack))
     }
 
-    /// Check whether the data already exists in the database before starting
-    pub fn check_assume_valid_target(&mut self, shared: &Shared) {
-        if let Some(ref target) = self.args.config.network.sync.assume_valid_target {
-            if shared.snapshot().block_exists(&target.pack()) {
-                info!("assume valid target is already in db, CKB will do full verification from now on");
-                self.args.config.network.sync.assume_valid_target.take();
-            }
-        }
-    }
-
     /// Start chain service, return ChainController
     pub fn start_chain_service(&self, shared: &Shared, table: ProposalTable) -> ChainController {
         let chain_service = ChainService::new(shared.clone(), table);


Test patch: