Skip to content

Commit 0e26fa3

Browse files
pgherveougithub-actions[bot]
authored andcommitted
[pallet-revive] prestate tracer (#8742)
- Add prestate tracer, with identical API as https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#prestate-tracer - Add coinbase runtime API to get the miner's address tested against geth with paritytech/evm-test-suite#93 fixes paritytech/revive-differential-tests#7 --------- Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent b47b963 commit 0e26fa3

File tree

18 files changed

+627
-78
lines changed

18 files changed

+627
-78
lines changed

prdoc/pr_8742.prdoc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
title: '[pallet-revive] prestate tracer'
2+
doc:
3+
- audience: Runtime Dev
4+
description: |-
5+
- Add prestate tracer
6+
- Add coinbase runtime API to get the miner's address
7+
tested against geth with https://github.com/paritytech/evm-test-suite/pull/93
8+
crates:
9+
- name: pallet-revive-eth-rpc
10+
bump: patch
11+
- name: pallet-revive
12+
bump: patch
1.47 KB
Binary file not shown.

substrate/frame/revive/rpc/src/apis/debug_apis.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub trait DebugRpc {
5353
async fn trace_call(
5454
&self,
5555
transaction: GenericTransaction,
56-
block: BlockNumberOrTag,
56+
block: BlockNumberOrTagOrHash,
5757
tracer_config: TracerConfig,
5858
) -> RpcResult<Trace>;
5959
}
@@ -109,7 +109,7 @@ impl DebugRpcServer for DebugRpcServerImpl {
109109
async fn trace_call(
110110
&self,
111111
transaction: GenericTransaction,
112-
block: BlockNumberOrTag,
112+
block: BlockNumberOrTagOrHash,
113113
tracer_config: TracerConfig,
114114
) -> RpcResult<Trace> {
115115
let TracerConfig { config, timeout } = tracer_config;

substrate/frame/revive/rpc/src/client.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,10 +595,10 @@ impl Client {
595595
pub async fn trace_call(
596596
&self,
597597
transaction: GenericTransaction,
598-
block: BlockNumberOrTag,
598+
block: BlockNumberOrTagOrHash,
599599
config: TracerType,
600600
) -> Result<Trace, ClientError> {
601-
let block_hash = self.block_hash_for_tag(block.into()).await?;
601+
let block_hash = self.block_hash_for_tag(block).await?;
602602
let runtime_api = self.runtime_api(block_hash);
603603
runtime_api.trace_call(transaction, config.clone()).await
604604
}
@@ -634,6 +634,7 @@ impl Client {
634634

635635
let header = block.header();
636636
let timestamp = extract_block_timestamp(block).await.unwrap_or_default();
637+
let block_author = runtime_api.block_author().await.ok().flatten().unwrap_or_default();
637638

638639
// TODO: remove once subxt is updated
639640
let parent_hash = header.parent_hash.0.into();
@@ -660,6 +661,7 @@ impl Client {
660661
hash: block.hash(),
661662
parent_hash,
662663
state_root,
664+
miner: block_author,
663665
transactions_root: extrinsics_root,
664666
number: header.number.into(),
665667
timestamp: timestamp.into(),

substrate/frame/revive/rpc/src/client/runtime_api.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ impl RuntimeApi {
9696
Ok(*gas_limit)
9797
}
9898

99+
/// Get the miner address
100+
pub async fn block_author(&self) -> Result<Option<H160>, ClientError> {
101+
let payload = subxt_client::apis().revive_api().block_author();
102+
let author = self.0.call(payload).await?;
103+
Ok(author)
104+
}
105+
99106
/// Get the trace for the given transaction index in the given block.
100107
pub async fn trace_tx(
101108
&self,

substrate/frame/revive/rpc/src/lib.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,6 @@ impl EthRpcServer for EthRpcServerImpl {
136136
transaction_hash: H256,
137137
) -> RpcResult<Option<ReceiptInfo>> {
138138
let receipt = self.client.receipt(&transaction_hash).await;
139-
log::debug!(
140-
target: LOG_TARGET,
141-
"transaction_receipt for {transaction_hash:?}: received: {received} - success: {success:?}",
142-
received = receipt.is_some(),
143-
success = receipt.as_ref().map(|r| r.status == Some(U256::one()))
144-
);
145139
Ok(receipt)
146140
}
147141

substrate/frame/revive/rpc/src/receipt_extractor.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ impl ReceiptExtractor {
164164
None
165165
};
166166

167-
log::debug!(target: LOG_TARGET, "Adding receipt for tx hash: {transaction_hash:?} - block: {block_number:?}");
168167
let receipt = ReceiptInfo::new(
169168
block_hash,
170169
block_number,
@@ -202,7 +201,11 @@ impl ReceiptExtractor {
202201
});
203202

204203
stream::iter(extrinsics)
205-
.map(|(ext, call)| async move { self.extract_from_extrinsic(block, ext, call).await })
204+
.map(|(ext, call)| async move {
205+
self.extract_from_extrinsic(block, ext, call).await.inspect_err(|err| {
206+
log::warn!(target: LOG_TARGET, "Error extracting extrinsic: {err:?}");
207+
})
208+
})
206209
.buffer_unordered(10)
207210
.collect::<Vec<Result<_, _>>>()
208211
.await

substrate/frame/revive/src/benchmarking.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -836,11 +836,7 @@ mod benchmarks {
836836
}
837837
assert_ok!(result);
838838

839-
let block_author = runtime
840-
.ext()
841-
.block_author()
842-
.map(|account| T::AddressMapper::to_address(&account))
843-
.unwrap_or(H160::zero());
839+
let block_author = runtime.ext().block_author().unwrap_or(H160::zero());
844840
assert_eq!(&memory[..], block_author.as_bytes());
845841
}
846842

substrate/frame/revive/src/evm/api/debug_rpc_types.rs

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616
// limitations under the License.
1717

1818
use crate::evm::Bytes;
19-
use alloc::{string::String, vec::Vec};
19+
use alloc::{collections::BTreeMap, string::String, vec::Vec};
2020
use codec::{Decode, Encode};
2121
use derive_more::From;
2222
use scale_info::TypeInfo;
23-
use serde::{Deserialize, Serialize};
23+
use serde::{
24+
ser::{SerializeMap, Serializer},
25+
Deserialize, Serialize,
26+
};
2427
use sp_core::{H160, H256, U256};
2528

2629
/// The type of tracer to use.
@@ -30,6 +33,9 @@ use sp_core::{H160, H256, U256};
3033
pub enum TracerType {
3134
/// A tracer that traces calls.
3235
CallTracer(Option<CallTracerConfig>),
36+
37+
/// A tracer that traces the prestate.
38+
PrestateTracer(Option<PrestateTracerConfig>),
3339
}
3440

3541
impl From<CallTracerConfig> for TracerType {
@@ -74,6 +80,26 @@ impl Default for CallTracerConfig {
7480
}
7581
}
7682

83+
/// The configuration for the prestate tracer.
84+
#[derive(Clone, Debug, Decode, Serialize, Deserialize, Encode, PartialEq, TypeInfo)]
85+
#[serde(default, rename_all = "camelCase")]
86+
pub struct PrestateTracerConfig {
87+
/// Whether to include the diff mode in the trace.
88+
pub diff_mode: bool,
89+
90+
/// Whether to include storage in the trace.
91+
pub disable_storage: bool,
92+
93+
/// Whether to include code in the trace.
94+
pub disable_code: bool,
95+
}
96+
97+
impl Default for PrestateTracerConfig {
98+
fn default() -> Self {
99+
Self { diff_mode: false, disable_storage: false, disable_code: false }
100+
}
101+
}
102+
77103
/// Serialization should support the following JSON format:
78104
///
79105
/// ```json
@@ -141,6 +167,85 @@ pub enum CallType {
141167
pub enum Trace {
142168
/// A call trace.
143169
Call(CallTrace),
170+
/// A prestate trace.
171+
Prestate(PrestateTrace),
172+
}
173+
174+
/// A prestate Trace
175+
#[derive(TypeInfo, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
176+
#[serde(untagged)]
177+
pub enum PrestateTrace {
178+
/// The Prestate mode returns the accounts necessary to execute a given transaction
179+
Prestate(BTreeMap<H160, PrestateTraceInfo>),
180+
181+
/// The diff mode returns the differences between the transaction's pre and post-state
182+
/// The result only contains the accounts that were modified by the transaction
183+
DiffMode {
184+
/// The state before the call.
185+
/// The accounts in the `pre` field will contain all of their basic fields, even if those
186+
/// fields have not been modified. For `storage` however, only non-empty slots that have
187+
/// been modified will be included
188+
pre: BTreeMap<H160, PrestateTraceInfo>,
189+
/// The state after the call.
190+
/// It only contains the specific fields that were actually modified during the transaction
191+
post: BTreeMap<H160, PrestateTraceInfo>,
192+
},
193+
}
194+
195+
impl PrestateTrace {
196+
/// Returns the pre and post trace info.
197+
pub fn state_mut(
198+
&mut self,
199+
) -> (&mut BTreeMap<H160, PrestateTraceInfo>, Option<&mut BTreeMap<H160, PrestateTraceInfo>>) {
200+
match self {
201+
PrestateTrace::Prestate(pre) => (pre, None),
202+
PrestateTrace::DiffMode { pre, post } => (pre, Some(post)),
203+
}
204+
}
205+
}
206+
207+
/// The info of a prestate trace.
208+
#[derive(
209+
TypeInfo, Default, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq,
210+
)]
211+
pub struct PrestateTraceInfo {
212+
/// The balance of the account.
213+
#[serde(skip_serializing_if = "Option::is_none")]
214+
pub balance: Option<U256>,
215+
/// The nonce of the account.
216+
#[serde(skip_serializing_if = "Option::is_none")]
217+
pub nonce: Option<u32>,
218+
/// The code of the contract account.
219+
#[serde(skip_serializing_if = "Option::is_none")]
220+
pub code: Option<Bytes>,
221+
/// The storage of the contract account.
222+
#[serde(
223+
skip_serializing_if = "BTreeMap::is_empty",
224+
serialize_with = "serialize_map_skip_none"
225+
)]
226+
pub storage: BTreeMap<Bytes, Option<Bytes>>,
227+
}
228+
229+
/// Serializes a map of `K -> Option<V>`, omitting any entries whose value is `None`.
230+
pub fn serialize_map_skip_none<S, K, V>(
231+
map: &BTreeMap<K, Option<V>>,
232+
serializer: S,
233+
) -> Result<S::Ok, S::Error>
234+
where
235+
S: Serializer,
236+
K: serde::Serialize,
237+
V: serde::Serialize,
238+
{
239+
let len = map.values().filter(|v| v.is_some()).count();
240+
let mut ser_map = serializer.serialize_map(Some(len))?;
241+
242+
for (key, opt_val) in map {
243+
if let Some(val) = opt_val {
244+
ser_map.serialize_entry(key, val)?;
245+
}
246+
}
247+
248+
ser_map.end()
144249
}
145250

146251
/// A smart contract execution call trace.

substrate/frame/revive/src/evm/runtime.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,12 +296,12 @@ pub trait EthExtra {
296296
InvalidTransaction::Call
297297
})?;
298298

299-
let signer = tx.recover_eth_address().map_err(|err| {
299+
let signer_addr = tx.recover_eth_address().map_err(|err| {
300300
log::debug!(target: LOG_TARGET, "Failed to recover signer: {err:?}");
301301
InvalidTransaction::BadProof
302302
})?;
303303

304-
let signer = <Self::Config as Config>::AddressMapper::to_fallback_account_id(&signer);
304+
let signer = <Self::Config as Config>::AddressMapper::to_fallback_account_id(&signer_addr);
305305
let GenericTransaction { nonce, chain_id, to, value, input, gas, gas_price, .. } =
306306
GenericTransaction::from_signed(tx, crate::GAS_PRICE.into(), None);
307307

@@ -364,7 +364,10 @@ pub trait EthExtra {
364364

365365
let mut info = call.get_dispatch_info();
366366
let function: CallOf<Self::Config> = call.into();
367-
let nonce = nonce.unwrap_or_default().try_into().map_err(|_| InvalidTransaction::Call)?;
367+
let nonce = nonce.unwrap_or_default().try_into().map_err(|_| {
368+
log::debug!(target: LOG_TARGET, "Failed to convert nonce");
369+
InvalidTransaction::Call
370+
})?;
368371
let gas_price = gas_price.unwrap_or_default();
369372

370373
let eth_fee = Pallet::<Self::Config>::evm_gas_to_fee(gas, gas_price)
@@ -393,6 +396,11 @@ pub trait EthExtra {
393396
.unwrap_or_default()
394397
.min(actual_fee);
395398

399+
crate::tracing::if_tracing(|tracer| {
400+
tracer.watch_address(&Pallet::<Self::Config>::block_author().unwrap_or_default());
401+
tracer.watch_address(&signer_addr);
402+
});
403+
396404
log::debug!(target: LOG_TARGET, "Created checked Ethereum transaction with nonce: {nonce:?} and tip: {tip:?}");
397405
Ok(CheckedExtrinsic {
398406
format: ExtrinsicFormat::Signed(signer.into(), Self::get_eth_extension(nonce, tip)),

0 commit comments

Comments
 (0)