Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rpc) Implement Filecoin.EthTraceBlock #4991

Merged
merged 76 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
5325bd5
Make schema work for EthTraceBlock
elmattic Nov 21, 2024
f43ce43
Merge branch 'main' into elmattic/eth-trace-block
elmattic Nov 26, 2024
549247a
Add eth trace module
elmattic Nov 27, 2024
b110384
Merge branch 'main' into elmattic/eth-trace-block
elmattic Nov 27, 2024
18cad8c
Implement more functions
elmattic Nov 27, 2024
e95328a
Add fil_actor_evm_state dependency and move to ExitCode v4
elmattic Nov 27, 2024
ab51150
Update to V4
elmattic Nov 27, 2024
d86fbb4
Implement more functions
elmattic Nov 27, 2024
bc90c41
Complete trace_native_create impl
elmattic Nov 28, 2024
fe79d4e
Scaffold the impl of decode_create_via_eam
elmattic Nov 28, 2024
ff3144f
Implement trace_is_evm_or_eam function
elmattic Nov 28, 2024
3bd0b8b
Implement trace_eth_create function
elmattic Nov 29, 2024
bfc11a7
Merge branch 'main' into elmattic/eth-trace-block
elmattic Dec 2, 2024
3c53381
Add trace_evm_private impl (wip)
elmattic Dec 2, 2024
e7ef3d7
Add build_traces impl
elmattic Dec 2, 2024
442144d
Add build_trace impl
elmattic Dec 2, 2024
2cd28a2
Remove from_repr usage
elmattic Dec 3, 2024
9990f05
Add header and minor cleanups
elmattic Dec 3, 2024
549d41d
Fix trace_eth_create function
elmattic Dec 3, 2024
5325467
Move most functions to private
elmattic Dec 3, 2024
65938f6
Fix build_trace
elmattic Dec 3, 2024
f4ef00d
Fix execution_trace function to return state root
elmattic Dec 3, 2024
29914cd
Merge branch 'main' into elmattic/eth-trace-block
elmattic Dec 3, 2024
fd5783c
Fix trace_evm_private function
elmattic Dec 4, 2024
3872fd2
Fix trace_evm_call function
elmattic Dec 4, 2024
efac542
Appease clippy
elmattic Dec 4, 2024
1dee860
Add comment and refactor
elmattic Dec 4, 2024
2517812
Fix gas_used member init
elmattic Dec 4, 2024
68ae2b2
Merge branch 'main' into elmattic/eth-trace-block
elmattic Dec 4, 2024
b0bc8ad
Merge branch 'main' into elmattic/eth-trace-block
elmattic Dec 16, 2024
f647a95
fix use import
elmattic Dec 16, 2024
944cd46
Finished decode_create_via_eam implementation
elmattic Dec 16, 2024
da843bf
Appease Clippy
elmattic Dec 16, 2024
36f2c0e
Remove hardcoded height
elmattic Dec 16, 2024
a6fd415
Fix empty string serialization
elmattic Dec 16, 2024
caba8d9
Fix tx positional index
elmattic Dec 16, 2024
f81a7af
Merge branch 'main' into elmattic/eth-trace-block
elmattic Dec 16, 2024
d7e6fc1
Fix build_trace return value
elmattic Dec 17, 2024
383083f
Fix subtraces member
elmattic Dec 17, 2024
831aeb5
Merge branch 'main' into elmattic/eth-trace-block
elmattic Dec 17, 2024
ba0e1cc
Fix missing variant
elmattic Dec 18, 2024
660fbdf
Appease clippy
elmattic Dec 18, 2024
15a6dc4
Merge branch 'main' into elmattic/eth-trace-block
elmattic Dec 18, 2024
e01a989
Change doc comment
elmattic Dec 18, 2024
68aa02a
Fix incorrect error code
elmattic Dec 19, 2024
eea85ca
Align exit code error string with lotus
elmattic Dec 19, 2024
8d02817
Avoid a few clones
elmattic Dec 19, 2024
30f99a7
Update CHANGELOG
elmattic Dec 19, 2024
416e6cf
Try to fix CI Check
elmattic Dec 19, 2024
8c0265e
Remove newline
elmattic Dec 19, 2024
5533594
Merge branch 'main' into elmattic/eth-trace-block
elmattic Dec 19, 2024
81a9f55
Merge branch 'main' into elmattic/eth-trace-block
elmattic Dec 20, 2024
3a24b5b
Remove Option type
elmattic Dec 20, 2024
b4d764c
Merge branch 'main' into elmattic/eth-trace-block
elmattic Dec 20, 2024
60baafb
Remove changes
elmattic Dec 20, 2024
070c190
Fix json
elmattic Dec 20, 2024
14b8f14
Fix json
elmattic Dec 20, 2024
8aad446
Merge branch 'main' into elmattic/eth-trace-block
elmattic Jan 6, 2025
bc60b69
Appease Clippy
elmattic Jan 6, 2025
ca2af72
Create eth::utils module
elmattic Jan 6, 2025
422f291
Remove Debug attribute
elmattic Jan 6, 2025
bc055bb
Move decode_{params|return} to utils
elmattic Jan 6, 2025
243b3c9
Move decode_payload to utils
elmattic Jan 6, 2025
a8349ac
Merge branch 'main' into elmattic/eth-trace-block
elmattic Jan 7, 2025
db2dd27
Merge branch 'main' into elmattic/eth-trace-block
elmattic Jan 7, 2025
55d82e7
Fix header
elmattic Jan 7, 2025
cfa0e96
Add decode payload tests
elmattic Jan 7, 2025
fee3058
Fix dev-dependencies
elmattic Jan 7, 2025
a41f450
Cleanup
elmattic Jan 7, 2025
2e31462
Merge branch 'main' into elmattic/eth-trace-block
elmattic Jan 7, 2025
4bb15ab
Merge branch 'main' into elmattic/eth-trace-block
elmattic Jan 8, 2025
d111d3d
Use fvm_shared identity hash definition
elmattic Jan 9, 2025
ff508b0
Remove magic number
elmattic Jan 9, 2025
b2c5430
Refactor to use exit code shim
elmattic Jan 9, 2025
284a66f
Fix doc comment
elmattic Jan 9, 2025
d1e1ac6
Merge branch 'main' into elmattic/eth-trace-block
elmattic Jan 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@

### Added

- [#4708](https://github.com/ChainSafe/forest/issues/4708) Add support for the
`Filecoin.EthTraceBlock` RPC method.

### Changed

### Removed
Expand Down
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ ez-jsonrpc-types = "0.5"
fil_actor_account_state = { version = "19" }
fil_actor_cron_state = { version = "19" }
fil_actor_datacap_state = { version = "19" }
fil_actor_eam_state = { version = "19" }
fil_actor_evm_state = { version = "19" }
fil_actor_init_state = { version = "19" }
fil_actor_market_state = { version = "19" }
Expand Down Expand Up @@ -227,6 +228,7 @@ ariadne = "0.5"
assert_cmd = "2"
bimap = "0.6"
cargo_metadata = "0.19"
cbor4ii = { version = "0.2", default-features = false, features = ["serde1"] }
criterion = { version = "0.5", features = ["async_tokio", "csv"] }
cs_serde_bytes = "0.12"
derive-quickcheck-arbitrary = "0.1"
Expand Down
13 changes: 13 additions & 0 deletions src/eth/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use anyhow::{bail, ensure, Context};
use bytes::BufMut;
use bytes::BytesMut;
use cbor4ii::core::{dec::Decode as _, utils::SliceReader, Value};
use fvm_shared4::METHOD_CONSTRUCTOR;
use num::{bigint::Sign, BigInt, Signed as _};
use num_derive::FromPrimitive;
use num_traits::cast::ToPrimitive;
use rlp::Rlp;

Expand All @@ -32,13 +34,24 @@ use super::{
EthChainId, EIP_1559_TX_TYPE, EIP_2930_TX_TYPE,
};
// As per `ref-fvm`, which hardcodes it as well.
#[derive(FromPrimitive)]
#[repr(u64)]
pub enum EAMMethod {
Constructor = METHOD_CONSTRUCTOR,
Create = 2,
Create2 = 3,
CreateExternal = 4,
}

#[derive(FromPrimitive)]
#[repr(u64)]
pub enum EVMMethod {
Constructor = METHOD_CONSTRUCTOR,
Resurrect = 2,
GetBytecode = 3,
GetBytecodeHash = 4,
GetStorageAt = 5,
InvokeContractDelegate = 6,
// As per `ref-fvm`:
// it is very unfortunate but the hasher creates a circular dependency, so we use the raw
// number.
Expand Down
132 changes: 73 additions & 59 deletions src/rpc/methods/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

mod eth_tx;
pub mod filter;
mod trace;
pub mod types;
mod utils;

use self::eth_tx::*;
use self::filter::hex_str_to_epoch;
Expand All @@ -22,12 +24,14 @@ use crate::interpreter::VMTrace;
use crate::lotus_json::{lotus_json_with_self, HasLotusJson};
use crate::message::{ChainMessage, Message as _, SignedMessage};
use crate::rpc::error::ServerError;
use crate::rpc::eth::types::EthBlockTrace;
use crate::rpc::types::{ApiTipsetKey, EventEntry, MessageLookup};
use crate::rpc::EthEventHandler;
use crate::rpc::{ApiPaths, Ctx, Permission, RpcMethod};
use crate::shim::actors::eam;
use crate::shim::actors::evm;
use crate::shim::actors::is_evm_actor;
use crate::shim::actors::system;
use crate::shim::actors::EVMActorStateLoad as _;
use crate::shim::address::{Address as FilecoinAddress, Protocol};
use crate::shim::crypto::Signature;
Expand All @@ -44,8 +48,6 @@ use crate::utils::db::BlockstoreExt as _;
use crate::utils::encoding::from_slice_with_fallback;
use crate::utils::multihash::prelude::*;
use anyhow::{anyhow, bail, Context, Error, Result};
use cbor4ii::core::dec::Decode as _;
use cbor4ii::core::Value;
use cid::Cid;
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::{RawBytes, CBOR, DAG_CBOR, IPLD_RAW};
Expand All @@ -56,6 +58,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use std::{ops::Add, sync::Arc};
use utils::{decode_payload, lookup_eth_address};

const MASKED_ID_PREFIX: [u8; 12] = [0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

Expand Down Expand Up @@ -185,7 +188,7 @@ impl EthHash {
let mh = MultihashCode::Blake2b256
.wrap(self.0.as_bytes())
.expect("should not fail");
Cid::new_v1(fvm_ipld_encoding::DAG_CBOR, mh)
Cid::new_v1(DAG_CBOR, mh)
}

pub fn empty_uncles() -> Self {
Expand Down Expand Up @@ -819,47 +822,6 @@ pub fn eth_tx_from_signed_eth_message(
Ok((from, tx))
}

fn lookup_eth_address<DB: Blockstore>(
addr: &FilecoinAddress,
state: &StateTree<DB>,
) -> Result<Option<EthAddress>> {
// Attempt to convert directly, if it's an f4 address.
if let Ok(eth_addr) = EthAddress::from_filecoin_address(addr) {
if !eth_addr.is_masked_id() {
return Ok(Some(eth_addr));
}
}

// Otherwise, resolve the ID addr.
let id_addr = match state.lookup_id(addr)? {
Some(id) => id,
_ => return Ok(None),
};

// Lookup on the target actor and try to get an f410 address.
let result = state.get_actor(addr);
if let Ok(Some(actor_state)) = result {
if let Some(addr) = actor_state.delegated_address {
if let Ok(eth_addr) = EthAddress::from_filecoin_address(&addr.into()) {
if !eth_addr.is_masked_id() {
// Conversable into an eth address, use it.
return Ok(Some(eth_addr));
}
}
} else {
// No delegated address -> use a masked ID address
}
} else if let Ok(None) = result {
// Not found -> use a masked ID address
} else {
// Any other error -> fail.
result?;
}

// Otherwise, use the masked address.
Ok(Some(EthAddress::from_actor_id(id_addr)))
}

/// See <https://docs.soliditylang.org/en/latest/abi-spec.html#function-selector-and-argument-encoding>
/// for ABI specification
fn encode_filecoin_params_as_abi(
Expand Down Expand Up @@ -916,21 +878,6 @@ fn encode_as_abi_helper(param1: u64, param2: u64, data: &[u8]) -> Vec<u8> {
buf
}

/// Decodes the payload using the given codec.
fn decode_payload(payload: &fvm_ipld_encoding::RawBytes, codec: u64) -> Result<EthBytes> {
match codec {
DAG_CBOR | CBOR => {
let mut reader = cbor4ii::core::utils::SliceReader::new(payload.bytes());
match Value::decode(&mut reader) {
Ok(Value::Bytes(bytes)) => Ok(EthBytes(bytes)),
_ => bail!("failed to read params byte array"),
}
}
IPLD_RAW => Ok(EthBytes(payload.to_vec())),
_ => bail!("decode_payload: unsupported codec {codec}"),
}
}

/// Convert a native message to an eth transaction.
///
/// - The state-tree must be from after the message was applied (ideally the following tipset).
Expand Down Expand Up @@ -2655,6 +2602,73 @@ impl RpcMethod<1> for EthGetLogs {
}
}

pub enum EthTraceBlock {}
impl RpcMethod<1> for EthTraceBlock {
const NAME: &'static str = "Filecoin.EthTraceBlock";
const NAME_ALIAS: Option<&'static str> = Some("eth_traceBlock");
const N_REQUIRED_PARAMS: usize = 1;
const PARAM_NAMES: [&'static str; 1] = ["block_param"];
const API_PATHS: ApiPaths = ApiPaths::V1;
const PERMISSION: Permission = Permission::Read;
type Params = (BlockNumberOrHash,);
type Ok = Vec<EthBlockTrace>;
async fn handle(
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(block_param,): Self::Params,
) -> Result<Self::Ok, ServerError> {
let ts = tipset_by_block_number_or_hash(ctx.chain_store(), block_param)?;

let (state_root, trace) = ctx.state_manager.execution_trace(&ts)?;

let state = StateTree::new_from_root(ctx.store_owned(), &state_root)?;

let cid = ts.key().cid()?;

let block_hash: EthHash = cid.into();

let mut all_traces = vec![];
let mut msg_idx = 0;
for ir in trace.into_iter() {
// ignore messages from system actor
if ir.msg.from == system::ADDRESS.into() {
continue;
}

msg_idx += 1;

let tx_hash = EthGetTransactionHashByCid::handle(ctx.clone(), (ir.msg_cid,)).await?;

let tx_hash = tx_hash
.with_context(|| format!("cannot find transaction hash for cid {}", ir.msg_cid))?;

let mut env = trace::base_environment(&state, &ir.msg.from)
.map_err(|e| format!("when processing message {}: {}", ir.msg_cid, e))?;

if let Some(execution_trace) = ir.execution_trace {
trace::build_traces(&mut env, &[], execution_trace)?;

for trace in env.traces {
all_traces.push(EthBlockTrace {
r#type: trace.r#type,
subtraces: trace.subtraces,
trace_address: trace.trace_address,
action: trace.action,
result: trace.result,
error: trace.error,

block_hash: block_hash.clone(),
block_number: ts.epoch(),
transaction_hash: tx_hash.clone(),
transaction_position: msg_idx as i64,
});
}
}
}

Ok(all_traces)
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
Loading
Loading