Skip to content

Commit

Permalink
Rework new_eth_tx_receipt implementation (#5176)
Browse files Browse the repository at this point in the history
  • Loading branch information
elmattic authored Jan 24, 2025
1 parent 2edb7a1 commit be4005a
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 63 deletions.
2 changes: 1 addition & 1 deletion .config/lychee.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ exclude = [
"faucet.calibnet.chainsafe-fil.io/funds.html",
# Bot protection
"jwt.io",
"forest-explorer.chainsafe.dev"
"forest-explorer.chainsafe.dev",
]
timeout = 30
max_retries = 6
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@

- [#5150](https://github.com/ChainSafe/forest/pull/5150) Fix incorrect prototype for the `Filecoin.EthGetBlockReceiptsLimited` RPC method.

- [#5006](https://github.com/ChainSafe/forest/issues/5006) Fix incorrect logs, logs bloom and event index for the `Filecoin.EthGetBlockReceipts` RPC method.

## Forest v.0.23.3 "Plumber"

Mandatory release for calibnet node operators. It fixes a sync error at epoch 2281645.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ dialoguer = "0.11"
digest = "0.10"
directories = "6"
displaydoc = "0.2"
ethereum-types = "0.15"
ethereum-types = { version = "0.15", features = ["ethbloom"] }
ez-jsonrpc-types = "0.5"
fil_actor_account_state = { version = "19" }
fil_actor_cron_state = { version = "19" }
Expand Down
8 changes: 6 additions & 2 deletions scripts/tests/api_compare/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,14 @@ services:
lotus wait-api --timeout 10m
# copy Lotus token to shared volume
cp /var/lib/lotus/token /data/lotus-token
# `sethead` right after `sync wait` to ensure the head is not set in middle of a sync
lotus sync wait
FULLNODE_API_INFO="$(cat /var/lib/lotus/token):/dns/lotus/tcp/${LOTUS_RPC_PORT}/http"
lotus chain sethead --epoch $(($(ls /data/*.car.zst | tail -n 1 | grep -Eo '[0-9]+' | tail -n 1) - 50))
SNAPSHOT_EPOCH="$(ls /data/*.car.zst | tail -n 1 | grep -Eo '[0-9]+' | tail -n 1)"
# backfill the index db first
lotus index validate-backfill --from $$SNAPSHOT_EPOCH --to $(($$SNAPSHOT_EPOCH - 300))
sleep 2
# `sethead` right after `sync wait` to ensure the head is not set in middle of a sync
lotus chain sethead --epoch $(($$SNAPSHOT_EPOCH - 50))
# wait for 30s to make sure the re-validation starts
sleep 30
lotus sync wait
Expand Down
2 changes: 0 additions & 2 deletions scripts/tests/api_compare/filter-list
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,3 @@
# TODO: https://github.com/ChainSafe/forest/issues/4996
!Filecoin.EthGetTransactionReceipt
!Filecoin.EthGetTransactionReceiptLimited
# TODO: https://github.com/ChainSafe/forest/issues/5006
!Filecoin.EthGetBlockReceipts
2 changes: 0 additions & 2 deletions scripts/tests/api_compare/filter-list-offline
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,3 @@
# TODO: https://github.com/ChainSafe/forest/issues/4996
!Filecoin.EthGetTransactionReceipt
!Filecoin.EthGetTransactionReceiptLimited
# TODO: https://github.com/ChainSafe/forest/issues/5006
!Filecoin.EthGetBlockReceipts
120 changes: 70 additions & 50 deletions src/rpc/methods/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ pub struct Bloom(

lotus_json_with_self!(Bloom);

impl Bloom {
pub fn accrue(&mut self, input: &[u8]) {
self.0.accrue(ethereum_types::BloomInput::Raw(input));
}
}

#[derive(
PartialEq,
Debug,
Expand Down Expand Up @@ -1043,65 +1049,91 @@ fn new_eth_tx<DB: Blockstore>(

async fn new_eth_tx_receipt<DB: Blockstore + Send + Sync + 'static>(
ctx: &Ctx<DB>,
tipset: &Arc<Tipset>,
tx: &ApiEthTx,
message_lookup: &MessageLookup,
msg_receipt: &Receipt,
) -> anyhow::Result<EthTxReceipt> {
let mut receipt = EthTxReceipt {
let mut tx_receipt = EthTxReceipt {
transaction_hash: tx.hash.clone(),
from: tx.from.clone(),
to: tx.to.clone(),
transaction_index: tx.transaction_index.clone(),
block_hash: tx.block_hash.clone(),
block_number: tx.block_number.clone(),
r#type: tx.r#type.clone(),
status: (message_lookup.receipt.exit_code().is_success() as u64).into(),
gas_used: message_lookup.receipt.gas_used().into(),
status: (msg_receipt.exit_code().is_success() as u64).into(),
gas_used: msg_receipt.gas_used().into(),
..EthTxReceipt::new()
};

let ts = ctx
.chain_store()
.load_required_tipset_or_heaviest(&message_lookup.tipset)?;

// This transaction is located in the parent tipset
let parent_ts = ctx
.chain_store()
.load_required_tipset_or_heaviest(ts.parents())?;

let base_fee = parent_ts.block_headers().first().parent_base_fee.clone();
tx_receipt.cumulative_gas_used = EthUint64::default();

let gas_fee_cap = tx.gas_fee_cap()?;
let gas_premium = tx.gas_premium()?;

let gas_outputs = GasOutputs::compute(
message_lookup.receipt.gas_used(),
msg_receipt.gas_used(),
tx.gas.clone().into(),
&base_fee,
&tipset.block_headers().first().parent_base_fee,
&gas_fee_cap.0.into(),
&gas_premium.0.into(),
);

let total_spent: BigInt = gas_outputs.total_spent().into();

let mut effective_gas_price = EthBigInt::default();
if message_lookup.receipt.gas_used() > 0 {
effective_gas_price = (total_spent / message_lookup.receipt.gas_used()).into();
if msg_receipt.gas_used() > 0 {
effective_gas_price = (total_spent / msg_receipt.gas_used()).into();
}
receipt.effective_gas_price = effective_gas_price;
tx_receipt.effective_gas_price = effective_gas_price;

if receipt.to.is_none() && message_lookup.receipt.exit_code().is_success() {
if tx_receipt.to.is_none() && msg_receipt.exit_code().is_success() {
// Create and Create2 return the same things.
let ret: eam::CreateExternalReturn =
from_slice_with_fallback(message_lookup.receipt.return_data().bytes())?;
from_slice_with_fallback(msg_receipt.return_data().bytes())?;

tx_receipt.contract_address = Some(ret.eth_address.0.into());
}

if msg_receipt.events_root().is_some() {
let logs =
eth_logs_for_block_and_transaction(ctx, tipset, &tx.block_hash, &tx.hash).await?;
if !logs.is_empty() {
tx_receipt.logs = logs;
}
}

receipt.contract_address = Some(ret.eth_address.0.into());
let mut bloom = Bloom::default();
for log in tx_receipt.logs.iter() {
for topic in log.topics.iter() {
bloom.accrue(topic.0.as_bytes());
}
bloom.accrue(log.address.0.as_bytes());
}
tx_receipt.logs_bloom = bloom.into();

Ok(tx_receipt)
}

async fn eth_logs_for_block_and_transaction<DB: Blockstore + Send + Sync + 'static>(
ctx: &Ctx<DB>,
ts: &Arc<Tipset>,
block_hash: &EthHash,
tx_hash: &EthHash,
) -> anyhow::Result<Vec<EthLog>> {
let spec = EthFilterSpec {
block_hash: Some(block_hash.clone()),
..Default::default()
};

let mut events = vec![];
EthEventHandler::collect_events(ctx, &parent_ts, None, &mut events).await?;
receipt.logs = eth_filter_logs_from_events(ctx, &events)?;
EthEventHandler::collect_events(ctx, ts, Some(&spec), &mut events).await?;

Ok(receipt)
let logs = eth_filter_logs_from_events(ctx, &events)?;
let out: Vec<_> = logs
.into_iter()
.filter(|log| &log.transaction_hash == tx_hash)
.collect();
Ok(out)
}

fn get_signed_message<DB: Blockstore>(ctx: &Ctx<DB>, message_cid: Cid) -> Result<SignedMessage> {
Expand Down Expand Up @@ -1229,46 +1261,34 @@ impl RpcMethod<2> for EthGetBlockByNumber {
async fn get_block_receipts<DB: Blockstore + Send + Sync + 'static>(
ctx: &Ctx<DB>,
block_hash: EthHash,
limit: Option<usize>,
// TODO(forest): https://github.com/ChainSafe/forest/issues/5177
_limit: Option<usize>,
) -> Result<Vec<EthTxReceipt>, ServerError> {
let ts = get_tipset_from_hash(ctx.chain_store(), &block_hash)?;
let ts_ref = Arc::new(ts);
let ts_key = ts_ref.key();
let (state_root, msgs_and_receipts) = execute_tipset(ctx, &ts_ref).await?;

let msgs_and_receipts = if let Some(limit) = limit {
msgs_and_receipts.into_iter().take(limit).collect()
} else {
msgs_and_receipts
};
// Execute the tipset to get the messages and receipts
let (state_root, msgs_and_receipts) = execute_tipset(ctx, &ts_ref).await?;

let mut receipts = Vec::with_capacity(msgs_and_receipts.len());
let state = StateTree::new_from_root(ctx.store_owned(), &state_root)?;
// Load the state tree
let state_tree = StateTree::new_from_root(ctx.store_owned(), &state_root)?;

let mut eth_receipts = Vec::with_capacity(msgs_and_receipts.len());
for (i, (msg, receipt)) in msgs_and_receipts.into_iter().enumerate() {
let return_dec = receipt.return_data().deserialize().unwrap_or(Ipld::Null);

let message_lookup = MessageLookup {
receipt,
tipset: ts_key.clone(),
height: ts_ref.epoch(),
message: msg.cid(),
return_dec,
};

let tx = new_eth_tx(
ctx,
&state,
&state_tree,
ts_ref.epoch(),
&ts_key.cid()?,
&msg.cid(),
i as u64,
)?;

let tx_receipt = new_eth_tx_receipt(ctx, &tx, &message_lookup).await?;
receipts.push(tx_receipt);
let receipt = new_eth_tx_receipt(ctx, &ts_ref, &tx, &receipt).await?;
eth_receipts.push(receipt);
}
Ok(receipts)
Ok(eth_receipts)
}

pub enum EthGetBlockReceipts {}
Expand Down Expand Up @@ -2340,7 +2360,7 @@ async fn get_eth_transaction_receipt(
let tx = new_eth_tx_from_message_lookup(&ctx, &message_lookup, None)
.with_context(|| format!("failed to convert {} into an Eth Tx", tx_hash))?;

let tx_receipt = new_eth_tx_receipt(&ctx, &tx, &message_lookup).await?;
let tx_receipt = new_eth_tx_receipt(&ctx, &tipset, &tx, &message_lookup.receipt).await?;

Ok(tx_receipt)
}
Expand Down
12 changes: 7 additions & 5 deletions src/rpc/methods/eth/filter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,9 @@ impl EthEventHandler {
messages.len() == events.len(),
"Length of messages and events do not match"
);
let mut event_count = 0;
for (i, (message, events)) in messages.iter().zip(events.into_iter()).enumerate() {
for (j, event) in events.iter().enumerate() {
for event in events.iter() {
let id_addr = Address::new_id(event.emitter());
let result = ctx
.state_manager
Expand All @@ -275,11 +276,10 @@ impl EthEventHandler {
resolved
} else {
// Skip event
event_count += 1;
continue;
};

let event_idx = j as u64;

let eth_emitter_addr = EthAddress::from_filecoin_address(&resolved)?;

let entries: Vec<crate::shim::executor::Entry> = event.event().entries();
Expand All @@ -289,7 +289,7 @@ impl EthEventHandler {
let matched = Self::do_match(spec, &eth_emitter_addr, &entries);
tracing::debug!(
"Event {} {}match filter topics",
event_idx,
event_count,
if matched { "" } else { "do not " }
);
matched
Expand All @@ -313,17 +313,19 @@ impl EthEventHandler {
let ce = CollectedEvent {
entries,
emitter_addr: resolved,
event_idx,
event_idx: event_count,
reverted: false,
height,
tipset_key: tipset_key.clone(),
msg_idx: i as u64,
msg_cid: message.cid(),
};
collected_events.push(ce);
event_count += 1;
}
}
}

Ok(())
}

Expand Down
6 changes: 6 additions & 0 deletions src/rpc/methods/eth/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ impl From<RawBytes> for EthBytes {
}
}

impl From<Bloom> for EthBytes {
fn from(value: Bloom) -> Self {
Self(value.0 .0.to_vec())
}
}

impl FromStr for EthBytes {
type Err = anyhow::Error;

Expand Down

0 comments on commit be4005a

Please sign in to comment.