Repository: nervosnetwork/ckb (language: rust)

Issue:
RPC `send_transaction` should not include stack trace in the RPC response
## Bug Report

When calling the send_transaction RPC, the response currently includes the full stack trace when an error occurs. This behavior is not ideal, as the stack trace information is better suited for internal debugging rather than being exposed in RPC responses. Including stack traces in RPC responses can also lead to larger payloads.

### Current Behavior
```
Exception: {'code': -302, 'message': 'TransactionFailedToVerify: Verification failed Transaction(InsufficientCellCapacity(Outputs[0]): expected occupied capacity (0x230489e00) <= capacity (0x64))', 'data': 'Verification(Error { kind: Transaction, inner: InsufficientCellCapacity(Outputs[0]): expected occupied capacity (0x230489e00) <= capacity (0x64)\n\nStack backtrace:\n   0: anyhow::error::<impl core::convert::From<E> for anyhow::Error>::from\n   1: ckb_verification::transaction_verifier::CapacityVerifier::verify\n   2: ckb_tx_pool::util::verify_rtx::{{closure}}::{{closure}}\n   3: ckb_tx_pool::util::verify_rtx::{{closure}}\n   4: ckb_tx_pool::process::<impl ckb_tx_pool::service::TxPoolService>::_process_tx::{{closure}}\n   5: ckb_tx_pool::process::<impl ckb_tx_pool::service::TxPoolService>::process_tx::{{closure}}\n   6: ckb_tx_pool::service::process::{{closure}}\n   7: ckb_async_runtime::Handle::spawn::{{closure}}\n   8: tokio::runtime::task::raw::poll\n   9: tokio::runtime::scheduler::multi_thread::worker::Context::run_task\n  10: tokio::runtime::scheduler::multi_thread::worker::run\n  11: tokio::runtime::task::raw::poll\n  12: std::sys_common::backtrace::__rust_begin_short_backtrace\n  13: core::ops::function::FnOnce::call_once{{vtable.shim}}\n  14: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once\n             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/alloc/src/boxed.rs:2007:9\n  15: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once\n             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/alloc/src/boxed.rs:2007:9\n  16: std::sys::unix::thread::Thread::new::thread_start\n             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/sys/unix/thread.rs:108:17\n  17: start_thread\n  18: clone })'}
```
pretty print the message:
```
TransactionFailedToVerify: Verification failed Transaction(InsufficientCellCapacity(Outputs[0]): expected occupied capacity (0x230489e00) <= capacity (0x64)), data: Verification(Error { kind: Transaction, inner: InsufficientCellCapacity(Outputs[0]): expected occupied capacity (0x230489e00) <= capacity (0x64)

Stack backtrace:
   0: anyhow::error::<impl core::convert::From<E> for anyhow::Error>::from
   1: ckb_verification::transaction_verifier::CapacityVerifier::verify
   2: ckb_tx_pool::util::verify_rtx::{{closure}}::{{closure}}
   3: ckb_tx_pool::util::verify_rtx::{{closure}}
   4: ckb_tx_pool::process::<impl ckb_tx_pool::service::TxPoolService>::_process_tx::{{closure}}
   5: ckb_tx_pool::process::<impl ckb_tx_pool::service::TxPoolService>::process_tx::{{closure}}
   6: ckb_tx_pool::service::process::{{closure}}
   7: ckb_async_runtime::Handle::spawn::{{closure}}
   8: tokio::runtime::task::raw::poll
   9: tokio::runtime::scheduler::multi_thread::worker::Context::run_task
  10: tokio::runtime::scheduler::multi_thread::worker::run
  11: tokio::runtime::task::raw::poll
  12: std::sys_common::backtrace::__rust_begin_short_backtrace
  13: core::ops::function::FnOnce::call_once{{vtable.shim}}
  14: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/alloc/src/boxed.rs:2007:9
  15: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/alloc/src/boxed.rs:2007:9
  16: std::sys::unix::thread::Thread::new::thread_start
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/sys/unix/thread.rs:108:17
  17: start_thread
  18: clone })
```

### Environment

- **CKB version**: [the output of `ckb --version`]
- **Chain**: [dev, staging, testnet, mainnet]
- **Operating system**: [Ubuntu 20.04, macOS 11.4, Windows 10]
- **Arch**: x64 [the output of `uname -a`]
- **Installation**: [GitHub Release, Built from source]

### Additional context/Screenshots
<!-- Add any other context about the problem here. If applicable, add ckb.toml and screenshots to help explain. -->

RPC `send_transaction` should not include stack trace in the RPC response
## Bug Report

When calling the send_transaction RPC, the response currently includes the full stack trace when an error occurs. This behavior is not ideal, as the stack trace information is better suited for internal debugging rather than being exposed in RPC responses. Including stack traces in RPC responses can also lead to larger payloads.

### Current Behavior
```
Exception: {'code': -302, 'message': 'TransactionFailedToVerify: Verification failed Transaction(InsufficientCellCapacity(Outputs[0]): expected occupied capacity (0x230489e00) <= capacity (0x64))', 'data': 'Verification(Error { kind: Transaction, inner: InsufficientCellCapacity(Outputs[0]): expected occupied capacity (0x230489e00) <= capacity (0x64)\n\nStack backtrace:\n   0: anyhow::error::<impl core::convert::From<E> for anyhow::Error>::from\n   1: ckb_verification::transaction_verifier::CapacityVerifier::verify\n   2: ckb_tx_pool::util::verify_rtx::{{closure}}::{{closure}}\n   3: ckb_tx_pool::util::verify_rtx::{{closure}}\n   4: ckb_tx_pool::process::<impl ckb_tx_pool::service::TxPoolService>::_process_tx::{{closure}}\n   5: ckb_tx_pool::process::<impl ckb_tx_pool::service::TxPoolService>::process_tx::{{closure}}\n   6: ckb_tx_pool::service::process::{{closure}}\n   7: ckb_async_runtime::Handle::spawn::{{closure}}\n   8: tokio::runtime::task::raw::poll\n   9: tokio::runtime::scheduler::multi_thread::worker::Context::run_task\n  10: tokio::runtime::scheduler::multi_thread::worker::run\n  11: tokio::runtime::task::raw::poll\n  12: std::sys_common::backtrace::__rust_begin_short_backtrace\n  13: core::ops::function::FnOnce::call_once{{vtable.shim}}\n  14: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once\n             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/alloc/src/boxed.rs:2007:9\n  15: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once\n             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/alloc/src/boxed.rs:2007:9\n  16: std::sys::unix::thread::Thread::new::thread_start\n             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/sys/unix/thread.rs:108:17\n  17: start_thread\n  18: clone })'}
```
pretty print the message:
```
TransactionFailedToVerify: Verification failed Transaction(InsufficientCellCapacity(Outputs[0]): expected occupied capacity (0x230489e00) <= capacity (0x64)), data: Verification(Error { kind: Transaction, inner: InsufficientCellCapacity(Outputs[0]): expected occupied capacity (0x230489e00) <= capacity (0x64)

Stack backtrace:
   0: anyhow::error::<impl core::convert::From<E> for anyhow::Error>::from
   1: ckb_verification::transaction_verifier::CapacityVerifier::verify
   2: ckb_tx_pool::util::verify_rtx::{{closure}}::{{closure}}
   3: ckb_tx_pool::util::verify_rtx::{{closure}}
   4: ckb_tx_pool::process::<impl ckb_tx_pool::service::TxPoolService>::_process_tx::{{closure}}
   5: ckb_tx_pool::process::<impl ckb_tx_pool::service::TxPoolService>::process_tx::{{closure}}
   6: ckb_tx_pool::service::process::{{closure}}
   7: ckb_async_runtime::Handle::spawn::{{closure}}
   8: tokio::runtime::task::raw::poll
   9: tokio::runtime::scheduler::multi_thread::worker::Context::run_task
  10: tokio::runtime::scheduler::multi_thread::worker::run
  11: tokio::runtime::task::raw::poll
  12: std::sys_common::backtrace::__rust_begin_short_backtrace
  13: core::ops::function::FnOnce::call_once{{vtable.shim}}
  14: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/alloc/src/boxed.rs:2007:9
  15: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/alloc/src/boxed.rs:2007:9
  16: std::sys::unix::thread::Thread::new::thread_start
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/sys/unix/thread.rs:108:17
  17: start_thread
  18: clone })
```

### Environment

- **CKB version**: [the output of `ckb --version`]
- **Chain**: [dev, staging, testnet, mainnet]
- **Operating system**: [Ubuntu 20.04, macOS 11.4, Windows 10]
- **Arch**: x64 [the output of `uname -a`]
- **Installation**: [GitHub Release, Built from source]

### Additional context/Screenshots
<!-- Add any other context about the problem here. If applicable, add ckb.toml and screenshots to help explain. -->



Discussion / hints:
https://github.com/dtolnay/anyhow/issues/346
https://github.com/dtolnay/anyhow/issues/346

Fix patch:
diff --git a/rpc/src/error.rs b/rpc/src/error.rs
index 753f747dbe..ba702456d6 100644
--- a/rpc/src/error.rs
+++ b/rpc/src/error.rs
@@ -121,6 +121,14 @@ pub enum RPCError {
     Indexer = -1200,
 }
 
+/// Removes the backtrace portion from an error string.
+fn remove_backtrace(err_str: &str) -> &str {
+    match err_str.find("\nStack backtrace:") {
+        Some(idx) => &err_str[..idx],
+        None => err_str,
+    }
+}
+
 impl RPCError {
     /// Invalid method parameter(s).
     pub fn invalid_params<T: Display>(message: T) -> Error {
@@ -158,10 +166,12 @@ impl RPCError {
     /// The parameter `err` is usually an std error. The Display form is used as the error message,
     /// and the Debug form is used as the data.
     pub fn custom_with_error<T: Display + Debug>(error_code: RPCError, err: T) -> Error {
+        let err_str_with_backtrace = format!("{err:?}");
+        let err_str = remove_backtrace(&err_str_with_backtrace);
         Error {
             code: ErrorCode::ServerError(error_code as i64),
             message: format!("{error_code:?}: {err}"),
-            data: Some(Value::String(format!("{err:?}"))),
+            data: Some(Value::String(err_str.to_string())),
         }
     }
 


Test patch: