1use std::sync::Arc;
2
3use crate::{
4 eth::block,
5 rpc::{RpcApiContext, RpcHandler},
6 types::{
7 block_identifier::{BlockIdentifier, BlockIdentifierOrHash},
8 transaction::{RpcTransaction, SendRawTransactionRequest},
9 },
10 utils::RpcErr,
11};
12use ethrex_blockchain::{Blockchain, vm::StoreVmDatabase};
13use ethrex_common::{
14 H256,
15 types::{AccessListEntry, BlockHash, BlockHeader, BlockNumber, GenericTransaction, TxKind},
16};
17
18use ethrex_rlp::encode::RLPEncode;
19use ethrex_storage::Store;
20
21use ethrex_vm::{ExecutionResult, backends::levm::get_max_allowed_gas_limit};
22use serde::Serialize;
23
24use serde_json::Value;
25use tracing::debug;
26
27pub const ESTIMATE_ERROR_RATIO: f64 = 0.015;
28pub const CALL_STIPEND: u64 = 2_300; pub const TRANSACTION_GAS: u64 = 21_000; pub struct CallRequest {
32 transaction: GenericTransaction,
33 block: Option<BlockIdentifierOrHash>,
34}
35
36pub struct GetTransactionByBlockNumberAndIndexRequest {
37 pub block: BlockIdentifier,
38 pub transaction_index: usize,
39}
40
41pub struct GetTransactionByBlockHashAndIndexRequest {
42 pub block: BlockHash,
43 pub transaction_index: usize,
44}
45
46pub struct GetTransactionByHashRequest {
47 pub transaction_hash: H256,
48}
49
50pub struct GetTransactionReceiptRequest {
51 pub transaction_hash: H256,
52}
53
54pub struct CreateAccessListRequest {
55 pub transaction: GenericTransaction,
56 pub block: Option<BlockIdentifier>,
57}
58pub struct EstimateGasRequest {
59 pub transaction: GenericTransaction,
60 pub block: Option<BlockIdentifier>,
61}
62
63pub struct GetRawTransaction {
64 pub transaction_hash: H256,
65}
66
67#[derive(Serialize)]
68#[serde(rename_all = "camelCase")]
69pub struct AccessListResult {
70 access_list: Vec<AccessListEntry>,
71 #[serde(skip_serializing_if = "Option::is_none")]
72 error: Option<String>,
73 #[serde(with = "ethrex_common::serde_utils::u64::hex_str")]
74 gas_used: u64,
75}
76
77impl RpcHandler for CallRequest {
78 fn parse(params: &Option<Vec<Value>>) -> Result<CallRequest, RpcErr> {
79 let params = params
80 .as_ref()
81 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
82 if params.is_empty() {
83 return Err(RpcErr::BadParams("No params provided".to_owned()));
84 }
85 if params.len() > 2 {
86 return Err(RpcErr::BadParams(format!(
87 "Expected one or two params and {} were provided",
88 params.len()
89 )));
90 }
91 let block = match params.get(1) {
92 Some(value) => Some(BlockIdentifierOrHash::parse(value.clone(), 1)?),
94 None => None,
95 };
96 Ok(CallRequest {
97 transaction: serde_json::from_value(params[0].clone())?,
98 block,
99 })
100 }
101 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
102 let block = self
103 .block
104 .clone()
105 .unwrap_or(BlockIdentifierOrHash::Identifier(BlockIdentifier::default()));
106 debug!("Requested call on block: {}", block);
107 let header = match block.resolve_block_header(&context.storage).await? {
108 Some(header) => header,
109 _ => return Ok(Value::Null),
111 };
112 let result = simulate_tx(
114 &self.transaction,
115 &header,
116 context.storage,
117 context.blockchain,
118 )?;
119 serde_json::to_value(format!("0x{:#x}", result.output()))
120 .map_err(|error| RpcErr::Internal(error.to_string()))
121 }
122}
123
124impl RpcHandler for GetTransactionByBlockNumberAndIndexRequest {
125 fn parse(
126 params: &Option<Vec<Value>>,
127 ) -> Result<GetTransactionByBlockNumberAndIndexRequest, RpcErr> {
128 let params = params
129 .as_ref()
130 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
131 if params.len() != 2 {
132 return Err(RpcErr::BadParams(format!(
133 "Expected two params and {} were provided",
134 params.len()
135 )));
136 };
137 let index_as_string: String = serde_json::from_value(params[1].clone())?;
138 Ok(GetTransactionByBlockNumberAndIndexRequest {
139 block: BlockIdentifier::parse(params[0].clone(), 0)?,
140 transaction_index: usize::from_str_radix(index_as_string.trim_start_matches("0x"), 16)
141 .map_err(|error| RpcErr::BadParams(error.to_string()))?,
142 })
143 }
144
145 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
146 debug!(
147 "Requested transaction at index: {} of block with number: {}",
148 self.transaction_index, self.block,
149 );
150 let block_number = match self.block.resolve_block_number(&context.storage).await? {
151 Some(block_number) => block_number,
152 _ => return Ok(Value::Null),
153 };
154 let block_body = match context.storage.get_block_body(block_number).await? {
155 Some(block_body) => block_body,
156 _ => return Ok(Value::Null),
157 };
158 let block_header = match context.storage.get_block_header(block_number)? {
159 Some(block_body) => block_body,
160 _ => return Ok(Value::Null),
161 };
162 let tx = match block_body.transactions.get(self.transaction_index) {
163 Some(tx) => tx,
164 None => return Ok(Value::Null),
165 };
166 let tx = RpcTransaction::build(
167 tx.clone(),
168 Some(block_number),
169 Some(block_header.hash()),
170 Some(self.transaction_index),
171 )?;
172 serde_json::to_value(tx).map_err(|error| RpcErr::Internal(error.to_string()))
173 }
174}
175
176impl RpcHandler for GetTransactionByBlockHashAndIndexRequest {
177 fn parse(
178 params: &Option<Vec<Value>>,
179 ) -> Result<GetTransactionByBlockHashAndIndexRequest, RpcErr> {
180 let params = params
181 .as_ref()
182 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
183 if params.len() != 2 {
184 return Err(RpcErr::BadParams(format!(
185 "Expected two param and {} were provided",
186 params.len()
187 )));
188 };
189 let index_as_string: String = serde_json::from_value(params[1].clone())?;
190 Ok(GetTransactionByBlockHashAndIndexRequest {
191 block: serde_json::from_value(params[0].clone())?,
192 transaction_index: usize::from_str_radix(index_as_string.trim_start_matches("0x"), 16)
193 .map_err(|error| RpcErr::BadParams(error.to_string()))?,
194 })
195 }
196 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
197 debug!(
198 "Requested transaction at index: {} of block with hash: {:#x}",
199 self.transaction_index, self.block,
200 );
201 let block_number = match context.storage.get_block_number(self.block).await? {
202 Some(number) => number,
203 _ => return Ok(Value::Null),
204 };
205 let block_body = match context.storage.get_block_body(block_number).await? {
206 Some(block_body) => block_body,
207 _ => return Ok(Value::Null),
208 };
209 let tx = match block_body.transactions.get(self.transaction_index) {
210 Some(tx) => tx,
211 None => return Ok(Value::Null),
212 };
213 let tx = RpcTransaction::build(
214 tx.clone(),
215 Some(block_number),
216 Some(self.block),
217 Some(self.transaction_index),
218 )?;
219 serde_json::to_value(tx).map_err(|error| RpcErr::Internal(error.to_string()))
220 }
221}
222
223impl RpcHandler for GetTransactionByHashRequest {
224 fn parse(params: &Option<Vec<Value>>) -> Result<GetTransactionByHashRequest, RpcErr> {
225 let params = params
226 .as_ref()
227 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
228 if params.len() != 1 {
229 return Err(RpcErr::BadParams(format!(
230 "Expected one param and {} were provided",
231 params.len()
232 )));
233 };
234 Ok(GetTransactionByHashRequest {
235 transaction_hash: serde_json::from_value(params[0].clone())?,
236 })
237 }
238 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
239 let storage = &context.storage;
240 debug!(
241 "Requested transaction with hash: {:#x}",
242 self.transaction_hash,
243 );
244 let transaction = if let Some((block_number, block_hash, index)) = storage
245 .get_transaction_location(self.transaction_hash)
246 .await?
247 {
248 let Some(tx) = storage
249 .get_transaction_by_location(block_hash, index)
250 .await?
251 else {
252 return Ok(Value::Null);
253 };
254 RpcTransaction::build(
255 tx,
256 Some(block_number),
257 Some(block_hash),
258 Some(index as usize),
259 )?
260 } else {
261 let Some(tx) = context
262 .blockchain
263 .mempool
264 .get_transaction_by_hash(self.transaction_hash)?
265 else {
266 return Ok(Value::Null);
267 };
268 RpcTransaction::build(tx, None, None, None)?
269 };
270 serde_json::to_value(transaction).map_err(|error| RpcErr::Internal(error.to_string()))
271 }
272}
273
274impl RpcHandler for GetTransactionReceiptRequest {
275 fn parse(params: &Option<Vec<Value>>) -> Result<GetTransactionReceiptRequest, RpcErr> {
276 let params = params
277 .as_ref()
278 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
279 if params.len() != 1 {
280 return Err(RpcErr::BadParams(format!(
281 "Expected one param and {} were provided",
282 params.len()
283 )));
284 };
285 Ok(GetTransactionReceiptRequest {
286 transaction_hash: serde_json::from_value(params[0].clone())?,
287 })
288 }
289 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
290 let storage = &context.storage;
291 debug!(
292 "Requested receipt for transaction {:#x}",
293 self.transaction_hash,
294 );
295 let (_block_number, block_hash, index) = match storage
296 .get_transaction_location(self.transaction_hash)
297 .await?
298 {
299 Some(location) => location,
300 _ => return Ok(Value::Null),
301 };
302 let block = match storage.get_block_by_hash(block_hash).await? {
303 Some(block) => block,
304 None => return Ok(Value::Null),
305 };
306 let receipts =
307 block::get_all_block_rpc_receipts(block.header, block.body, storage, Some(index))
308 .await?;
309
310 serde_json::to_value(receipts.get(index as usize))
311 .map_err(|error| RpcErr::Internal(error.to_string()))
312 }
313}
314
315impl RpcHandler for CreateAccessListRequest {
316 fn parse(params: &Option<Vec<Value>>) -> Result<CreateAccessListRequest, RpcErr> {
317 let params = params
318 .as_ref()
319 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
320 if params.is_empty() {
321 return Err(RpcErr::BadParams("No params provided".to_owned()));
322 }
323 if params.len() > 2 {
324 return Err(RpcErr::BadParams(format!(
325 "Expected one or two params and {} were provided",
326 params.len()
327 )));
328 }
329 let block = match params.get(1) {
330 Some(value) => Some(BlockIdentifier::parse(value.clone(), 1)?),
332 None => None,
333 };
334 Ok(CreateAccessListRequest {
335 transaction: serde_json::from_value(params[0].clone())?,
336 block,
337 })
338 }
339 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
340 let block = self.block.clone().unwrap_or_default();
341 debug!("Requested access list creation for tx on block: {}", block);
342 let block_number = match block.resolve_block_number(&context.storage).await? {
343 Some(block_number) => block_number,
344 _ => return Ok(Value::Null),
345 };
346 let header = match context.storage.get_block_header(block_number)? {
347 Some(header) => header,
348 _ => return Ok(Value::Null),
350 };
351
352 let vm_db = StoreVmDatabase::new(context.storage.clone(), header.clone())?;
353 let mut vm = context.blockchain.new_evm(vm_db)?;
354
355 let (gas_used, access_list, error) = vm.create_access_list(&self.transaction, &header)?;
357 let result = AccessListResult {
358 access_list: access_list
359 .into_iter()
360 .map(|(address, storage_keys)| AccessListEntry {
361 address,
362 storage_keys,
363 })
364 .collect(),
365 error,
366 gas_used,
367 };
368
369 serde_json::to_value(result).map_err(|error| RpcErr::Internal(error.to_string()))
370 }
371}
372
373impl RpcHandler for GetRawTransaction {
374 fn parse(params: &Option<Vec<Value>>) -> Result<Self, RpcErr> {
375 let params = params
376 .as_ref()
377 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
378 if params.len() != 1 {
379 return Err(RpcErr::BadParams(format!(
380 "Expected one param and {} were provided",
381 params.len()
382 )));
383 };
384
385 let transaction_str: String = serde_json::from_value(params[0].clone())?;
386 if !transaction_str.starts_with("0x") {
387 return Err(RpcErr::BadHexFormat(0));
388 }
389
390 Ok(GetRawTransaction {
391 transaction_hash: serde_json::from_value(params[0].clone())?,
392 })
393 }
394
395 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
396 let mut tx = context
397 .storage
398 .get_transaction_by_hash(self.transaction_hash)
399 .await?;
400 if tx.is_none() {
401 tx = context
402 .blockchain
403 .mempool
404 .get_transaction_by_hash(self.transaction_hash)?;
405 }
406 let tx = match tx {
407 Some(tx) => tx,
408 _ => return Ok(Value::Null),
409 };
410 serde_json::to_value(format!("0x{}", &hex::encode(tx.encode_to_vec())))
411 .map_err(|error| RpcErr::Internal(error.to_string()))
412 }
413}
414
415impl RpcHandler for EstimateGasRequest {
416 fn parse(params: &Option<Vec<Value>>) -> Result<EstimateGasRequest, RpcErr> {
417 let params = params
418 .as_ref()
419 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
420 if params.is_empty() {
421 return Err(RpcErr::BadParams("No params provided".to_owned()));
422 }
423 if params.len() > 2 {
424 return Err(RpcErr::BadParams(format!(
425 "Expected one or two params and {} were provided",
426 params.len()
427 )));
428 }
429 let block = match params.get(1) {
430 Some(value) => Some(BlockIdentifier::parse(value.clone(), 1)?),
432 None => None,
433 };
434 Ok(EstimateGasRequest {
435 transaction: serde_json::from_value(params[0].clone())?,
436 block,
437 })
438 }
439 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
440 let storage = &context.storage;
441 let blockchain = &context.blockchain;
442 let block = self.block.clone().unwrap_or_default();
443 let chain_config = storage.get_chain_config();
444
445 debug!("Requested estimate on block: {}", block);
446 let block_header = match block.resolve_block_header(storage).await? {
447 Some(header) => header,
448 _ => return Ok(Value::Null),
450 };
451
452 let current_fork = chain_config.fork(block_header.timestamp);
453
454 let transaction = match self.transaction.nonce {
455 Some(_nonce) => self.transaction.clone(),
456 None => {
457 let transaction_nonce = storage
458 .get_nonce_by_account_address(block_header.number, self.transaction.from)
459 .await?;
460
461 let mut cloned_transaction = self.transaction.clone();
462 cloned_transaction.nonce = transaction_nonce;
463 cloned_transaction
464 }
465 };
466
467 if let TxKind::Call(address) = transaction.to {
469 let account_info = storage
470 .get_account_info(block_header.number, address)
471 .await?;
472 let code = account_info.map(|info| storage.get_account_code(info.code_hash));
473 if code.is_none() {
474 let mut value_transfer_transaction = transaction.clone();
475 value_transfer_transaction.gas = Some(TRANSACTION_GAS);
476 let result: Result<ExecutionResult, RpcErr> = simulate_tx(
477 &value_transfer_transaction,
478 &block_header,
479 storage.clone(),
480 blockchain.clone(),
481 );
482 if let Ok(ExecutionResult::Success { .. }) = result {
483 return serde_json::to_value(format!("{TRANSACTION_GAS:#x}"))
484 .map_err(|error| RpcErr::Internal(error.to_string()));
485 }
486 }
487 }
488
489 let highest_gas_limit = get_max_allowed_gas_limit(block_header.gas_limit, current_fork);
491 let mut highest_gas_limit = match transaction.gas {
492 Some(gas) => gas.min(highest_gas_limit),
493 None => highest_gas_limit,
494 };
495
496 if !transaction.gas_price.is_zero() {
497 highest_gas_limit = recap_with_account_balances(
498 highest_gas_limit,
499 &transaction,
500 storage,
501 block_header.number,
502 )
503 .await?;
504 }
505
506 let mut transaction = transaction.clone();
508 transaction.gas = Some(highest_gas_limit);
509 let result = simulate_tx(
510 &transaction,
511 &block_header,
512 storage.clone(),
513 blockchain.clone(),
514 )?;
515
516 let gas_used = result.gas_used();
517 let gas_refunded = result.gas_refunded();
518
519 let optimistic_limit = (gas_used + gas_refunded + CALL_STIPEND) * 64 / 63;
521 let mut lowest_gas_limit = gas_used.saturating_sub(1);
522 let mut middle_gas_limit = (optimistic_limit + lowest_gas_limit) / 2;
523
524 while lowest_gas_limit + 1 < highest_gas_limit {
525 if (highest_gas_limit - lowest_gas_limit) as f64 / (highest_gas_limit as f64)
526 < ESTIMATE_ERROR_RATIO
527 {
528 break;
529 };
530
531 if middle_gas_limit > lowest_gas_limit * 2 {
532 middle_gas_limit = lowest_gas_limit * 2;
534 }
535 transaction.gas = Some(middle_gas_limit);
536
537 let result = simulate_tx(
538 &transaction,
539 &block_header,
540 storage.clone(),
541 blockchain.clone(),
542 );
543 if let Ok(ExecutionResult::Success { .. }) = result {
544 highest_gas_limit = middle_gas_limit;
545 } else {
546 lowest_gas_limit = middle_gas_limit;
547 };
548 middle_gas_limit = (highest_gas_limit + lowest_gas_limit) / 2;
549 }
550
551 serde_json::to_value(format!("{highest_gas_limit:#x}"))
552 .map_err(|error| RpcErr::Internal(error.to_string()))
553 }
554}
555
556async fn recap_with_account_balances(
557 highest_gas_limit: u64,
558 transaction: &GenericTransaction,
559 storage: &Store,
560 block_number: BlockNumber,
561) -> Result<u64, RpcErr> {
562 let account_balance = storage
563 .get_account_info(block_number, transaction.from)
564 .await?
565 .map(|acc| acc.balance)
566 .unwrap_or_default();
567 let account_gas = account_balance.saturating_sub(transaction.value) / transaction.gas_price;
568 let account_gas = u64::try_from(account_gas).unwrap_or(highest_gas_limit);
570 Ok(highest_gas_limit.min(account_gas))
571}
572
573fn simulate_tx(
574 transaction: &GenericTransaction,
575 block_header: &BlockHeader,
576 storage: Store,
577 blockchain: Arc<Blockchain>,
578) -> Result<ExecutionResult, RpcErr> {
579 let vm_db = StoreVmDatabase::new(storage, block_header.clone())?;
580 let mut vm = blockchain.new_evm(vm_db)?;
581
582 match vm.simulate_tx_from_generic(transaction, block_header)? {
583 ExecutionResult::Revert {
584 gas_used: _,
585 output,
586 } => Err(RpcErr::Revert {
587 data: format!("0x{output:#x}"),
588 }),
589 ExecutionResult::Halt { reason, gas_used } => Err(RpcErr::Halt { reason, gas_used }),
590 success => Ok(success),
591 }
592}
593
594impl RpcHandler for SendRawTransactionRequest {
595 fn parse(params: &Option<Vec<Value>>) -> Result<SendRawTransactionRequest, RpcErr> {
596 let data = get_transaction_data(params)?;
597
598 let transaction = SendRawTransactionRequest::decode_canonical(&data)
599 .map_err(|error| RpcErr::BadParams(error.to_string()))?;
600
601 if matches!(transaction, SendRawTransactionRequest::PrivilegedL2(_)) {
602 return Err(RpcErr::BadParams("Invalid transaction type".to_string()));
603 }
604
605 Ok(transaction)
606 }
607
608 async fn handle(&self, context: RpcApiContext) -> Result<Value, RpcErr> {
609 let hash = if let SendRawTransactionRequest::EIP4844(wrapped_blob_tx) = self {
610 context
611 .blockchain
612 .add_blob_transaction_to_pool(
613 wrapped_blob_tx.tx.clone(),
614 wrapped_blob_tx.blobs_bundle.clone(),
615 )
616 .await
617 } else {
618 context
619 .blockchain
620 .add_transaction_to_pool(self.to_transaction())
621 .await
622 }?;
623 serde_json::to_value(format!("{hash:#x}"))
624 .map_err(|error| RpcErr::Internal(error.to_string()))
625 }
626}
627
628fn get_transaction_data(rpc_req_params: &Option<Vec<Value>>) -> Result<Vec<u8>, RpcErr> {
629 let params = rpc_req_params
630 .as_ref()
631 .ok_or(RpcErr::BadParams("No params provided".to_owned()))?;
632 if params.len() != 1 {
633 return Err(RpcErr::BadParams(format!(
634 "Expected one param and {} were provided",
635 params.len()
636 )));
637 };
638
639 let str_data = serde_json::from_value::<String>(params[0].clone())?;
640 let str_data = str_data
641 .strip_prefix("0x")
642 .ok_or(RpcErr::BadParams("Params are note 0x prefixed".to_owned()))?;
643 hex::decode(str_data).map_err(|error| RpcErr::BadParams(error.to_string()))
644}