Skip to content

Commit 2d3dedf

Browse files
authored
feat: eth_getTransactionByHash returns executed, but not mined transactions (#1240)
1 parent bb7f7a7 commit 2d3dedf

File tree

6 files changed

+96
-43
lines changed

6 files changed

+96
-43
lines changed

src/eth/primitives/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ mod storage_point_in_time;
4343
mod transaction_execution;
4444
mod transaction_input;
4545
mod transaction_mined;
46+
mod transaction_stage;
4647
mod unix_time;
4748
mod wei;
4849

@@ -98,6 +99,7 @@ pub use transaction_execution::LocalTransactionExecution;
9899
pub use transaction_execution::TransactionExecution;
99100
pub use transaction_input::TransactionInput;
100101
pub use transaction_mined::TransactionMined;
102+
pub use transaction_stage::TransactionStage;
101103
pub use unix_time::UnixTime;
102104
pub use wei::Wei;
103105

src/eth/primitives/transaction_mined.rs

-14
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::hash::Hash as HashTrait;
33
use ethers_core::types::Transaction as EthersTransaction;
44
use ethers_core::types::TransactionReceipt as EthersReceipt;
55
use itertools::Itertools;
6-
use serde_json::Value as JsonValue;
76

87
use crate::eth::primitives::BlockNumber;
98
use crate::eth::primitives::EvmExecution;
@@ -14,7 +13,6 @@ use crate::eth::primitives::Index;
1413
use crate::eth::primitives::LogMined;
1514
use crate::eth::primitives::TransactionInput;
1615
use crate::ext::OptionExt;
17-
use crate::ext::ResultExt;
1816
use crate::if_else;
1917

2018
/// Transaction that was executed by the EVM and added to a block.
@@ -76,18 +74,6 @@ impl TransactionMined {
7674
pub fn is_success(&self) -> bool {
7775
self.execution.is_success()
7876
}
79-
80-
/// Serializes itself to JSON-RPC transaction format.
81-
pub fn to_json_rpc_transaction(self) -> JsonValue {
82-
let json_rpc_format: EthersTransaction = self.into();
83-
serde_json::to_value(json_rpc_format).expect_infallible()
84-
}
85-
86-
/// Serializes itself to JSON-RPC receipt format.
87-
pub fn to_json_rpc_receipt(self) -> JsonValue {
88-
let json_rpc_format: EthersReceipt = self.into();
89-
serde_json::to_value(json_rpc_format).expect_infallible()
90-
}
9177
}
9278

9379
// -----------------------------------------------------------------------------
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use ethers_core::types::Transaction as EthersTransaction;
2+
use ethers_core::types::TransactionReceipt as EthersReceipt;
3+
use serde_json::Value as JsonValue;
4+
5+
use crate::eth::primitives::TransactionExecution;
6+
use crate::eth::primitives::TransactionMined;
7+
use crate::ext::ResultExt;
8+
9+
/// Stages that a transaction can be in.
10+
#[allow(clippy::large_enum_variant)]
11+
#[derive(Debug, Clone, derive_new::new)]
12+
pub enum TransactionStage {
13+
/// Transaction was executed, but is awaiting to be mined to a block.
14+
Executed(TransactionExecution),
15+
16+
/// Transaction that was added to a mined block.
17+
Mined(TransactionMined),
18+
}
19+
20+
impl TransactionStage {
21+
/// Serializes itself to JSON-RPC transaction format.
22+
pub fn to_json_rpc_transaction(self) -> JsonValue {
23+
match self {
24+
TransactionStage::Executed(TransactionExecution::Local(tx)) => {
25+
let json_rpc_payload: EthersTransaction = tx.input.into();
26+
serde_json::to_value(json_rpc_payload).expect_infallible()
27+
}
28+
TransactionStage::Executed(TransactionExecution::External(tx)) => {
29+
// remove block information because we don't know to which local block the transaction will be added to.
30+
let mut ethers_tx = tx.tx.0;
31+
ethers_tx.block_number = None;
32+
ethers_tx.block_hash = None;
33+
serde_json::to_value(ethers_tx).expect_infallible()
34+
}
35+
TransactionStage::Mined(tx) => {
36+
let json_rpc_payload: EthersTransaction = tx.into();
37+
serde_json::to_value(json_rpc_payload).expect_infallible()
38+
}
39+
}
40+
}
41+
42+
/// Serializes itself to JSON-RPC receipt format.
43+
pub fn to_json_rpc_receipt(self) -> JsonValue {
44+
match self {
45+
TransactionStage::Executed(_) => JsonValue::Null,
46+
TransactionStage::Mined(tx) => {
47+
let json_rpc_format: EthersReceipt = tx.into();
48+
serde_json::to_value(json_rpc_format).expect_infallible()
49+
}
50+
}
51+
}
52+
}

src/eth/rpc/rpc_server.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -393,19 +393,18 @@ fn eth_get_uncle_by_block_hash_and_index(_: Params<'_>, _: &RpcContext, _: &Exte
393393
// Transaction
394394
// -----------------------------------------------------------------------------
395395

396-
#[tracing::instrument(name = "rpc::eth_getTransactionByHash", skip_all, fields(hash))]
396+
#[tracing::instrument(name = "rpc::eth_getTransactionByHash", skip_all, fields(hash, found))]
397397
fn eth_get_transaction_by_hash(params: Params<'_>, ctx: Arc<RpcContext>, _: Extensions) -> anyhow::Result<JsonValue, RpcError> {
398398
let (_, hash) = next_rpc_param::<Hash>(params.sequence())?;
399-
400399
Span::with(|s| s.rec_str("hash", &hash));
401400

402-
let mined = ctx.storage.read_mined_transaction(&hash)?;
401+
let tx = ctx.storage.read_transaction(&hash)?;
403402
Span::with(|s| {
404-
s.record("found", mined.is_some());
403+
s.record("found", tx.is_some());
405404
});
406405

407-
match mined {
408-
Some(mined) => Ok(mined.to_json_rpc_transaction()),
406+
match tx {
407+
Some(tx) => Ok(tx.to_json_rpc_transaction()),
409408
None => Ok(JsonValue::Null),
410409
}
411410
}
@@ -415,12 +414,12 @@ fn eth_get_transaction_receipt(params: Params<'_>, ctx: Arc<RpcContext>, _: Exte
415414
let (_, hash) = next_rpc_param::<Hash>(params.sequence())?;
416415
Span::with(|s| s.rec_str("hash", &hash));
417416

418-
let mined = ctx.storage.read_mined_transaction(&hash)?;
417+
let tx = ctx.storage.read_transaction(&hash)?;
419418
Span::with(|s| {
420-
s.record("found", mined.is_some());
419+
s.record("found", tx.is_some());
421420
});
422421

423-
match mined {
422+
match tx {
424423
Some(mined_transaction) => Ok(mined_transaction.to_json_rpc_receipt()),
425424
None => Ok(JsonValue::Null),
426425
}

src/eth/storage/stratus_storage.rs

+32-18
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::eth::primitives::SlotIndex;
1818
use crate::eth::primitives::SlotSample;
1919
use crate::eth::primitives::StoragePointInTime;
2020
use crate::eth::primitives::TransactionExecution;
21-
use crate::eth::primitives::TransactionMined;
21+
use crate::eth::primitives::TransactionStage;
2222
use crate::eth::storage::PermanentStorage;
2323
use crate::eth::storage::TemporaryStorage;
2424
use crate::infra::metrics;
@@ -226,21 +226,21 @@ impl StratusStorage {
226226
// read from temp only if present
227227
if point_in_time.is_present() {
228228
tracing::debug!(storage = %label::TEMP, %address, "reading account");
229-
let result = timed(|| self.temp.read_account(address)).with(|m| {
229+
let temp_account = timed(|| self.temp.read_account(address)).with(|m| {
230230
metrics::inc_storage_read_account(m.elapsed, label::TEMP, point_in_time, m.result.is_ok());
231-
});
232-
if let Some(account) = result? {
231+
})?;
232+
if let Some(account) = temp_account {
233233
tracing::debug!(storage = %label::TEMP, %address, "account found in temporary storage");
234234
return Ok(account);
235235
}
236236
}
237237

238238
// always read from perm if necessary
239239
tracing::debug!(storage = %label::PERM, %address, "reading account");
240-
let result = timed(|| self.perm.read_account(address, point_in_time)).with(|m| {
240+
let perm_account = timed(|| self.perm.read_account(address, point_in_time)).with(|m| {
241241
metrics::inc_storage_read_account(m.elapsed, label::PERM, point_in_time, m.result.is_ok());
242-
});
243-
match result? {
242+
})?;
243+
match perm_account {
244244
Some(account) => {
245245
tracing::debug!(%address, "account found in permanent storage");
246246
Ok(account)
@@ -263,21 +263,21 @@ impl StratusStorage {
263263
// read from temp only if present
264264
if point_in_time.is_present() {
265265
tracing::debug!(storage = %label::TEMP, %address, %index, "reading slot");
266-
let result = timed(|| self.temp.read_slot(address, index)).with(|m| {
266+
let temp_slot = timed(|| self.temp.read_slot(address, index)).with(|m| {
267267
metrics::inc_storage_read_slot(m.elapsed, label::TEMP, point_in_time, m.result.is_ok());
268-
});
269-
if let Some(slot) = result? {
268+
})?;
269+
if let Some(slot) = temp_slot {
270270
tracing::debug!(storage = %label::TEMP, %address, %index, value = %slot.value, "slot found in temporary storage");
271271
return Ok(slot);
272272
}
273273
}
274274

275275
// always read from perm if necessary
276276
tracing::debug!(storage = %label::PERM, %address, %index, ?point_in_time, "reading slot");
277-
let result = timed(|| self.perm.read_slot(address, index, point_in_time)).with(|m| {
277+
let perm_slot = timed(|| self.perm.read_slot(address, index, point_in_time)).with(|m| {
278278
metrics::inc_storage_read_slot(m.elapsed, label::PERM, point_in_time, m.result.is_ok());
279-
});
280-
match result? {
279+
})?;
280+
match perm_slot {
281281
Some(slot) => {
282282
tracing::debug!(%address, %index, value = %slot.value, "slot found in permanent storage");
283283
Ok(slot)
@@ -347,13 +347,27 @@ impl StratusStorage {
347347
}
348348

349349
#[tracing::instrument(name = "storage::read_transaction", skip_all, fields(hash))]
350-
pub fn read_mined_transaction(&self, hash: &Hash) -> anyhow::Result<Option<TransactionMined>> {
350+
pub fn read_transaction(&self, hash: &Hash) -> anyhow::Result<Option<TransactionStage>> {
351351
Span::with(|s| s.rec_str("hash", hash));
352-
tracing::debug!(storage = %label::PERM, %hash, "reading transaction");
353352

354-
timed(|| self.perm.read_transaction(hash)).with(|m| {
355-
metrics::inc_storage_read_mined_transaction(m.elapsed, label::PERM, m.result.is_ok());
356-
})
353+
// read from temp
354+
tracing::debug!(storage = %label::TEMP, %hash, "reading transaction");
355+
let temp_tx = timed(|| self.temp.read_transaction(hash)).with(|m| {
356+
metrics::inc_storage_read_transaction(m.elapsed, label::TEMP, m.result.is_ok());
357+
})?;
358+
if let Some(tx_temp) = temp_tx {
359+
return Ok(Some(TransactionStage::new_executed(tx_temp)));
360+
}
361+
362+
// read from perm
363+
tracing::debug!(storage = %label::PERM, %hash, "reading transaction");
364+
let perm_tx = timed(|| self.perm.read_transaction(hash)).with(|m| {
365+
metrics::inc_storage_read_transaction(m.elapsed, label::PERM, m.result.is_ok());
366+
})?;
367+
match perm_tx {
368+
Some(tx) => Ok(Some(TransactionStage::new_mined(tx))),
369+
None => Ok(None),
370+
}
357371
}
358372

359373
#[tracing::instrument(name = "storage::read_logs", skip_all)]

src/infra/metrics/metrics_definitions.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ metrics! {
4242
"Time to execute storage read_slot operation."
4343
histogram_duration storage_read_slot{storage, point_in_time, success},
4444

45-
"Time to execute storage read_mined_transaction operation."
46-
histogram_duration storage_read_mined_transaction{storage, success}
45+
"Time to execute storage read_transaction operation."
46+
histogram_duration storage_read_transaction{storage, success}
4747
}
4848

4949
// Storage writes.

0 commit comments

Comments
 (0)