Skip to content

Commit bd26b9a

Browse files
authored
Merge pull request #3336 from Pana/feat/txMetaRpc
feat: add a custom espace rpc endpoint to return tx execution extra block info
2 parents 0b32ceb + 5d332c4 commit bd26b9a

File tree

7 files changed

+160
-42
lines changed

7 files changed

+160
-42
lines changed

crates/cfxcore/core/src/consensus/consensus_graph/onchain_blocks_provider.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use cfx_parameters::consensus::*;
77

88
use cfx_types::H256;
99

10-
use primitives::{compute_block_number, EpochNumber};
10+
use primitives::{compute_block_number, BlockHashOrEpochNumber, EpochNumber};
1111
use std::cmp::min;
1212

1313
impl ConsensusGraph {
@@ -69,6 +69,46 @@ impl ConsensusGraph {
6969
})
7070
}
7171

72+
pub fn get_block_hashes_by_epoch_or_block_hash(
73+
&self, block_hash_or_epoch: BlockHashOrEpochNumber,
74+
) -> Result<Vec<H256>, ProviderBlockError> {
75+
let hashes = match block_hash_or_epoch {
76+
BlockHashOrEpochNumber::EpochNumber(e) => {
77+
self.get_block_hashes_by_epoch(e)?
78+
}
79+
BlockHashOrEpochNumber::BlockHashWithOption {
80+
hash: h,
81+
require_pivot,
82+
} => {
83+
// verify the block header exists
84+
let _ = self
85+
.data_manager()
86+
.block_header_by_hash(&h)
87+
.ok_or("block not found")?;
88+
89+
let e =
90+
self.get_block_epoch_number(&h).ok_or("block not found")?;
91+
92+
let hashes = self.get_block_hashes_by_epoch(e.into())?;
93+
94+
// if the provided hash is not the pivot hash,
95+
// and require_pivot is true or None(default to true)
96+
// abort
97+
let pivot_hash = *hashes.last().ok_or("inconsistent state")?;
98+
99+
if require_pivot.unwrap_or(true) && (h != pivot_hash) {
100+
bail!(ProviderBlockError::Common(
101+
"require_pivot check failed".into()
102+
));
103+
}
104+
105+
hashes
106+
}
107+
};
108+
Ok(hashes)
109+
}
110+
111+
/// Get the pivot block hash of the specified epoch number
72112
pub fn get_hash_from_epoch_number(
73113
&self, epoch_number: EpochNumber,
74114
) -> Result<H256, ProviderBlockError> {

crates/client/src/rpc/impls/cfx/cfx_handler.rs

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,44 +1701,9 @@ impl RpcImpl {
17011701
) -> CoreResult<Option<Vec<Vec<RpcReceipt>>>> {
17021702
info!("RPC Request: cfx_getEpochReceipts({:?})", epoch);
17031703

1704-
let hashes = match epoch {
1705-
BlockHashOrEpochNumber::EpochNumber(e) => {
1706-
self.consensus.get_block_hashes_by_epoch(e.into())?
1707-
}
1708-
BlockHashOrEpochNumber::BlockHashWithOption {
1709-
hash: h,
1710-
require_pivot,
1711-
} => {
1712-
if self
1713-
.consensus
1714-
.data_manager()
1715-
.block_header_by_hash(&h)
1716-
.is_none()
1717-
{
1718-
bail!(invalid_params("block_hash", "block not found"));
1719-
}
1720-
1721-
let e = match self.get_block_epoch_number(&h) {
1722-
Some(e) => e,
1723-
None => return Ok(None), // not executed
1724-
};
1725-
1726-
let hashes = self.consensus.get_block_hashes_by_epoch(
1727-
primitives::EpochNumber::Number(e),
1728-
)?;
1729-
1730-
// if the provided hash is not the pivot hash,
1731-
// and require_pivot is true or None(default to true)
1732-
// abort
1733-
let pivot_hash = *hashes.last().ok_or("Inconsistent state")?;
1734-
1735-
if require_pivot.unwrap_or(true) && (h != pivot_hash) {
1736-
bail!(pivot_assumption_failed(h, pivot_hash));
1737-
}
1738-
1739-
hashes
1740-
}
1741-
};
1704+
let hashes = self
1705+
.consensus
1706+
.get_block_hashes_by_epoch_or_block_hash(epoch.into())?;
17421707

17431708
let pivot_hash = *hashes.last().ok_or("Inconsistent state")?;
17441709
let mut epoch_receipts = vec![];

crates/rpc/rpc-eth-api/src/debug.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use alloy_rpc_types_trace::geth::{
22
GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
33
TraceResult,
44
};
5-
use cfx_rpc_eth_types::{BlockId, TransactionRequest};
5+
use cfx_rpc_eth_types::{BlockId, BlockProperties, TransactionRequest};
66
use cfx_types::H256;
77
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
88

@@ -36,4 +36,11 @@ pub trait DebugApi {
3636
&self, request: TransactionRequest, block_number: Option<BlockId>,
3737
opts: Option<GethDebugTracingCallOptions>,
3838
) -> RpcResult<GethTrace>;
39+
40+
/// Returns block properties needed for validate transaction execution
41+
/// This method will not return properties for phantom transactions
42+
#[method(name = "blockProperties")]
43+
async fn debug_block_properties(
44+
&self, block_number: BlockId,
45+
) -> RpcResult<Option<Vec<BlockProperties>>>;
3946
}

crates/rpc/rpc-eth-impl/src/debug.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use alloy_rpc_types_trace::geth::{
66
};
77
use async_trait::async_trait;
88
use cfx_rpc_eth_api::DebugApiServer;
9-
use cfx_rpc_eth_types::{BlockId, TransactionRequest};
9+
use cfx_rpc_eth_types::{BlockId, BlockProperties, TransactionRequest};
1010
use cfx_rpc_utils::error::jsonrpsee_error_helpers::invalid_params_msg;
1111
use cfx_types::{AddressSpaceUtil, Space, H256, U256};
1212
use cfxcore::{
@@ -17,7 +17,7 @@ use jsonrpsee::core::RpcResult;
1717
use primitives::{
1818
Block, BlockHashOrEpochNumber, BlockHeaderBuilder, EpochNumber,
1919
};
20-
use std::sync::Arc;
20+
use std::{sync::Arc, vec};
2121

2222
pub struct DebugApi {
2323
consensus: SharedConsensusGraph,
@@ -276,4 +276,46 @@ impl DebugApiServer for DebugApi {
276276
self.trace_call(request, block_number, opts)
277277
.map_err(|e| e.into())
278278
}
279+
280+
async fn debug_block_properties(
281+
&self, block_number: BlockId,
282+
) -> RpcResult<Option<Vec<BlockProperties>>> {
283+
let hashes = self
284+
.consensus
285+
.get_block_hashes_by_epoch_or_block_hash(block_number.into())
286+
.map_err(|err| invalid_params_msg(&err.to_string()))?;
287+
288+
let blocks = self
289+
.consensus
290+
.data_manager()
291+
.blocks_by_hash_list(&hashes, false)
292+
.ok_or_else(|| invalid_params_msg("blocks should exist"))?;
293+
294+
let mut res = vec![];
295+
296+
for block in blocks {
297+
let block_props = BlockProperties {
298+
tx_hash: None,
299+
inner_block_hash: block.hash(),
300+
coinbase: *block.block_header.author(),
301+
difficulty: *block.block_header.difficulty(),
302+
gas_limit: *block.block_header.gas_limit(),
303+
timestamp: block.block_header.timestamp().into(),
304+
base_fee_per_gas: block
305+
.block_header
306+
.base_price()
307+
.map(|v| v.in_space(Space::Ethereum).clone()),
308+
};
309+
310+
for tx in block.transactions.iter() {
311+
if tx.space() == Space::Ethereum {
312+
let mut props = block_props.clone();
313+
props.tx_hash = Some(tx.hash());
314+
res.push(props);
315+
}
316+
}
317+
}
318+
319+
Ok(Some(res))
320+
}
279321
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use cfx_types::{Address, H256, U256, U64};
2+
use serde::Serialize;
3+
4+
/// Block properties needed for transaction execution
5+
#[derive(Debug, Serialize, Clone)]
6+
#[serde(rename_all = "camelCase")]
7+
pub struct BlockProperties {
8+
pub tx_hash: Option<H256>,
9+
pub inner_block_hash: H256, // hash of the DAG block
10+
pub coinbase: Address,
11+
pub difficulty: U256,
12+
pub gas_limit: U256,
13+
pub timestamp: U64,
14+
pub base_fee_per_gas: Option<U256>,
15+
}

crates/rpc/rpc-eth-types/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod access_list;
22
mod authorization;
33
mod block;
44
mod block_number;
5+
mod block_properties;
56
mod call;
67
mod errors;
78
pub mod eth_pubsub;
@@ -22,6 +23,7 @@ pub use access_list::*;
2223
pub use authorization::{Authorization, SignedAuthorization};
2324
pub use block::{Block, BlockOverrides, Header};
2425
pub use block_number::BlockId;
26+
pub use block_properties::BlockProperties;
2527
pub use call::*;
2628
pub use cfx_rpc_primitives::{Bytes, Index, U64};
2729
pub use errors::Error;

docs/rpc/espace-custom.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# eSpace Custom RPC endpoints
2+
3+
Some RPC endpoints unique to Conflux eSpace.
4+
5+
## debug
6+
7+
### debug_blockProperties
8+
9+
The structure of Conflux's blockchain is based on DAG, and an Epoch usually consists of multiple blocks. Transaction execution is performed epoch by epoch. In eSpace, an eSpace block actually corresponds to an Epoch in the DAG, which means it contains transactions from multiple blocks in the DAG.
10+
11+
Due to this feature, transactions within a single Conflux eSpace block may have different block contexts (coinbase, timestamp, difficulty) during execution. Currently, the block properties affected by this issue include:
12+
13+
1. coinbase
14+
2. difficulty/prevrandao
15+
3. gaslimit: current block gaslimit
16+
4. timestamp
17+
5. basefee
18+
19+
Conflux eSpace does not support blobhash and blobbasefee because Conflux does not support EIP-4844. These two opcodes will return default values during execution.
20+
21+
Some services require verification of transaction execution, so we provide the `debug_blockProperties` interface to query the block properties of all transactions in an eSpace block.
22+
23+
```json
24+
{
25+
"jsonrpc": "2.0",
26+
"method": "debug_blockProperties",
27+
"params": ["0x1B4"], // block number or hash
28+
"id": 1
29+
}
30+
31+
{
32+
"jsonrpc": "2.0",
33+
"id": 1,
34+
"result": [
35+
{
36+
"txHash": "0x3719bb0b4385a7e0266d1e266166d821b351a38a1a78f2e36df99c73bbbc15ae",
37+
// the DAG block hash where this transaction is included
38+
"innerBlockHash": "0x446012a81945dc9cde4eca03697e43d5f80beed878d78b9829b54cb9a1f9f7a4",
39+
"coinbase": "0x1d69d968e3673e188b2d2d42b6a385686186258f",
40+
"difficulty": "0x4",
41+
"gasLimit": "0x3938700",
42+
"timestamp": "0x68ee1848",
43+
"baseFeePerGas": "0x1"
44+
}
45+
]
46+
}
47+
```

0 commit comments

Comments
 (0)