mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
fix(rpc): support both input and data fields (#3911)
This commit is contained in:
@ -19,11 +19,9 @@ pub struct CallRequest {
|
||||
pub gas: Option<U256>,
|
||||
/// Value
|
||||
pub value: Option<U256>,
|
||||
/// Transaction data
|
||||
///
|
||||
/// This accepts both `input` and `data`
|
||||
#[serde(alias = "input")]
|
||||
pub data: Option<Bytes>,
|
||||
/// Transaction input data
|
||||
#[serde(default, flatten)]
|
||||
pub input: CallInput,
|
||||
/// Nonce
|
||||
pub nonce: Option<U256>,
|
||||
/// chain id
|
||||
@ -44,6 +42,71 @@ impl CallRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper type that supports both `data` and `input` fields that map to transaction input data.
|
||||
///
|
||||
/// This is done for compatibility reasons where older implementations used `data` instead of the
|
||||
/// newer, recommended `input` field.
|
||||
///
|
||||
/// If both fields are set, it is expected that they contain the same value, otherwise an error is
|
||||
/// returned.
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct CallInput {
|
||||
/// Transaction data
|
||||
pub input: Option<Bytes>,
|
||||
/// Transaction data
|
||||
///
|
||||
/// This is the same as `input` but is used for backwards compatibility: <https://github.com/ethereum/go-ethereum/issues/15628>
|
||||
pub data: Option<Bytes>,
|
||||
}
|
||||
|
||||
impl CallInput {
|
||||
/// Consumes the type and returns the optional input data.
|
||||
///
|
||||
/// Returns an error if both `data` and `input` fields are set and not equal.
|
||||
pub fn try_into_unique_input(self) -> Result<Option<Bytes>, CallInputError> {
|
||||
let Self { input, data } = self;
|
||||
match (input, data) {
|
||||
(Some(input), Some(data)) if input == data => Ok(Some(input)),
|
||||
(Some(_), Some(_)) => Err(CallInputError::default()),
|
||||
(Some(input), None) => Ok(Some(input)),
|
||||
(None, Some(data)) => Ok(Some(data)),
|
||||
(None, None) => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes the type and returns the optional input data.
|
||||
///
|
||||
/// Returns an error if both `data` and `input` fields are set and not equal.
|
||||
pub fn unique_input(&self) -> Result<Option<&Bytes>, CallInputError> {
|
||||
let Self { input, data } = self;
|
||||
match (input, data) {
|
||||
(Some(input), Some(data)) if input == data => Ok(Some(input)),
|
||||
(Some(_), Some(_)) => Err(CallInputError::default()),
|
||||
(Some(input), None) => Ok(Some(input)),
|
||||
(None, Some(data)) => Ok(Some(data)),
|
||||
(None, None) => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Bytes> for CallInput {
|
||||
fn from(input: Bytes) -> Self {
|
||||
Self { input: Some(input), data: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<Bytes>> for CallInput {
|
||||
fn from(input: Option<Bytes>) -> Self {
|
||||
Self { input, data: None }
|
||||
}
|
||||
}
|
||||
|
||||
/// Error thrown when both `data` and `input` fields are set and not equal.
|
||||
#[derive(Debug, Default, thiserror::Error)]
|
||||
#[error("both \"data\" and \"input\" are set and not equal. Please use \"input\" to pass transaction call data")]
|
||||
#[non_exhaustive]
|
||||
pub struct CallInputError;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -53,4 +116,23 @@ mod tests {
|
||||
let s = r#"{"accessList":[],"data":"0x0902f1ac","to":"0xa478c2975ab1ea89e8196811f51a7b7ade33eb11","type":"0x02"}"#;
|
||||
let _req = serde_json::from_str::<CallRequest>(s).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_unique_call_input() {
|
||||
let s = r#"{"accessList":[],"data":"0x0902f1ac", "input":"0x0902f1ac","to":"0xa478c2975ab1ea89e8196811f51a7b7ade33eb11","type":"0x02"}"#;
|
||||
let req = serde_json::from_str::<CallRequest>(s).unwrap();
|
||||
assert!(req.input.try_into_unique_input().unwrap().is_some());
|
||||
|
||||
let s = r#"{"accessList":[],"data":"0x0902f1ac","to":"0xa478c2975ab1ea89e8196811f51a7b7ade33eb11","type":"0x02"}"#;
|
||||
let req = serde_json::from_str::<CallRequest>(s).unwrap();
|
||||
assert!(req.input.try_into_unique_input().unwrap().is_some());
|
||||
|
||||
let s = r#"{"accessList":[],"input":"0x0902f1ac","to":"0xa478c2975ab1ea89e8196811f51a7b7ade33eb11","type":"0x02"}"#;
|
||||
let req = serde_json::from_str::<CallRequest>(s).unwrap();
|
||||
assert!(req.input.try_into_unique_input().unwrap().is_some());
|
||||
|
||||
let s = r#"{"accessList":[],"data":"0x0902f1ac", "input":"0x0902f1","to":"0xa478c2975ab1ea89e8196811f51a7b7ade33eb11","type":"0x02"}"#;
|
||||
let req = serde_json::from_str::<CallRequest>(s).unwrap();
|
||||
assert!(req.input.try_into_unique_input().is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ mod work;
|
||||
|
||||
pub use account::*;
|
||||
pub use block::*;
|
||||
pub use call::CallRequest;
|
||||
pub use call::{CallInput, CallInputError, CallRequest};
|
||||
pub use fee::{FeeHistory, TxGasAndReward};
|
||||
pub use filter::*;
|
||||
pub use index::Index;
|
||||
|
||||
@ -423,7 +423,7 @@ where
|
||||
gas_price: Some(U256::from(gas_price)),
|
||||
max_fee_per_gas: Some(U256::from(max_fee_per_gas)),
|
||||
value: request.value,
|
||||
data: request.data.clone(),
|
||||
input: request.data.clone().into(),
|
||||
nonce: request.nonce,
|
||||
chain_id: Some(chain_id),
|
||||
access_list: request.access_list.clone(),
|
||||
|
||||
@ -7,7 +7,7 @@ use jsonrpsee::{
|
||||
};
|
||||
use reth_primitives::{abi::decode_revert_reason, Address, Bytes, U256};
|
||||
use reth_revm::tracing::js::JsInspectorError;
|
||||
use reth_rpc_types::{error::EthRpcErrorCode, BlockError};
|
||||
use reth_rpc_types::{error::EthRpcErrorCode, BlockError, CallInputError};
|
||||
use reth_transaction_pool::error::{InvalidPoolTransactionError, PoolError};
|
||||
use revm::primitives::{EVMError, ExecutionResult, Halt, OutOfGasError};
|
||||
use std::time::Duration;
|
||||
@ -90,6 +90,8 @@ pub enum EthApiError {
|
||||
/// Internal Error thrown by the javascript tracer
|
||||
#[error("{0}")]
|
||||
InternalJsTracerError(String),
|
||||
#[error(transparent)]
|
||||
CallInputError(#[from] CallInputError),
|
||||
}
|
||||
|
||||
impl From<EthApiError> for ErrorObject<'static> {
|
||||
@ -124,6 +126,7 @@ impl From<EthApiError> for ErrorObject<'static> {
|
||||
}
|
||||
err @ EthApiError::InternalTracingError => internal_rpc_err(err.to_string()),
|
||||
err @ EthApiError::InternalEthError => internal_rpc_err(err.to_string()),
|
||||
err @ EthApiError::CallInputError(_) => invalid_params_rpc_err(err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,7 +282,7 @@ pub(crate) fn create_txn_env(block_env: &BlockEnv, request: CallRequest) -> EthR
|
||||
max_priority_fee_per_gas,
|
||||
gas,
|
||||
value,
|
||||
data,
|
||||
input,
|
||||
nonce,
|
||||
access_list,
|
||||
chain_id,
|
||||
@ -308,7 +308,7 @@ pub(crate) fn create_txn_env(block_env: &BlockEnv, request: CallRequest) -> EthR
|
||||
gas_priority_fee: max_priority_fee_per_gas,
|
||||
transact_to: to.map(TransactTo::Call).unwrap_or_else(TransactTo::create),
|
||||
value: value.unwrap_or_default(),
|
||||
data: data.map(|data| data.0).unwrap_or_default(),
|
||||
data: input.try_into_unique_input()?.map(|data| data.0).unwrap_or_default(),
|
||||
chain_id: chain_id.map(|c| c.as_u64()),
|
||||
access_list: access_list.map(AccessList::flattened).unwrap_or_default(),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user