Repository: nervosnetwork/ckb (language: rust)

Issue:
Avoid Banning Peer on `ckb_vm::Error::External` Triggered by Ctrl-C Signal
## Bug Report

```
2024-10-29 02:02:47.915 +00:00 GlobalRt-20 INFO ckb_tx_pool::process  after_process Byte32(0x58aff34dd6bbac94032aee41ab59fa6232c4139ea135ffab7f7ed014eb89ac12) SessionId(588) remote reject: Verification failed Script(TransactionScriptError { source: Inputs[0].Type, cause: VM Internal Error: External("stopped") }) 
2024-10-29 02:02:47.915 +00:00 GlobalRt-20 INFO ckb_network::network  Ban peer "/ip4/54.168.12.92/tcp/30967/p2p/QmYx1orST2KkEbwmwGEkpSvcJjVenhKEphXhci85zcg6VX" for 259200 seconds, reason: reject Verification failed Script(TransactionScriptError { source: Inputs[0].Type, cause: VM Internal Error: External("stopped") })
```

https://github.com/nervosnetwork/ckb/blob/8cb49e4d727f337a2c80e210507a9e277bab3391/script/src/verify.rs#L1200-L1204


When chunk_run_with_signal receives ChunkCommand::Stop, it raises ckb_vm::Error::External, leading NetworkState to ban the peer. Since this error can result from a Ctrl-C signal, banning the peer is unnecessary.


### Expected Behavior
Handle ckb_vm::Error::External triggered by signals (e.g., Ctrl-C) without banning the peer.



### Environment

- **CKB version**: 0.119.0
- **Chain**: testnet
- **Operating system**: [Ubuntu 20.04, macOS 11.4, Windows 10]
- **Arch**: x64 [the output of `uname -a`]
- **Installation**: GitHub Release




Discussion / hints:

Fix patch:
diff --git a/error/src/internal.rs b/error/src/internal.rs
index 71e99cd214..f73f7103ce 100644
--- a/error/src/internal.rs
+++ b/error/src/internal.rs
@@ -50,6 +50,9 @@ pub enum InternalErrorKind {
     /// The feature is disabled or is conflicted with the configuration
     Config,
 
+    /// Interrupts, such as a Ctrl-C signal
+    Interrupts,
+
     /// Other system error
     Other,
 }
diff --git a/script/src/error.rs b/script/src/error.rs
index cac50b738d..c4778562d7 100644
--- a/script/src/error.rs
+++ b/script/src/error.rs
@@ -1,5 +1,5 @@
 use crate::types::{ScriptGroup, ScriptGroupType};
-use ckb_error::{prelude::*, Error, ErrorKind};
+use ckb_error::{prelude::*, Error, ErrorKind, InternalErrorKind};
 use ckb_types::core::{Cycle, ScriptHashType};
 use ckb_types::packed::{Byte32, Script};
 use ckb_vm::Error as VMInternalError;
@@ -44,6 +44,10 @@ pub enum ScriptError {
     #[error("VM Internal Error: {0:?}")]
     VMInternalError(VMInternalError),
 
+    /// Interrupts, such as a Ctrl-C signal
+    #[error("VM Interrupts")]
+    Interrupts,
+
     /// Other errors raised in script execution process
     #[error("Other Error: {0}")]
     Other(String),
@@ -182,7 +186,11 @@ impl ScriptError {
 
 impl From<TransactionScriptError> for Error {
     fn from(error: TransactionScriptError) -> Self {
-        ErrorKind::Script.because(error)
+        match error.cause {
+            ScriptError::Interrupts => ErrorKind::Internal
+                .because(InternalErrorKind::Interrupts.other(ScriptError::Interrupts.to_string())),
+            _ => ErrorKind::Script.because(error),
+        }
     }
 }
 
diff --git a/script/src/verify.rs b/script/src/verify.rs
index 6551ffa7f3..f2bf0d44a0 100644
--- a/script/src/verify.rs
+++ b/script/src/verify.rs
@@ -1186,6 +1186,7 @@ where
         let mut scheduler = Scheduler::new(tx_data, version, self.syscalls_generator.clone());
         let map_vm_internal_error = |error: VMInternalError| match error {
             VMInternalError::CyclesExceeded => ScriptError::ExceededMaximumCycles(max_cycles),
+            VMInternalError::External(reason) if reason.eq("stopped") => ScriptError::Interrupts,
             _ => ScriptError::VMInternalError(error),
         };
 


Test patch:
diff --git a/script/src/verify/tests/ckb_latest/features_since_v2023.rs b/script/src/verify/tests/ckb_latest/features_since_v2023.rs
index d5eb783ca0..ddb0f0627d 100644
--- a/script/src/verify/tests/ckb_latest/features_since_v2023.rs
+++ b/script/src/verify/tests/ckb_latest/features_since_v2023.rs
@@ -540,8 +540,11 @@ async fn check_spawn_suspend_shutdown() {
         .verify_complete_async(script_version, &rtx, &mut command_rx, true)
         .await;
     assert!(res.is_err());
-    let err = res.unwrap_err().to_string();
-    assert!(err.contains("VM Internal Error: External(\"stopped\")"));
+    let err = res.unwrap_err();
+    assert!(err.to_string().contains("VM Interrupts"));
+
+    let reject = ckb_types::core::tx_pool::Reject::Verification(err);
+    assert!(!reject.is_malformed_tx());
 }
 
 #[test]