Skip to content

Commit 74a5f0c

Browse files
fix: contract deployment via forge failing (#6118)
Co-authored-by: hanabi1224 <[email protected]>
1 parent 051fb19 commit 74a5f0c

File tree

10 files changed

+101
-24
lines changed

10 files changed

+101
-24
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ Non-mandatory release supporting new API methods and addressing a critical panic
5555

5656
- [#6325](https://github.com/ChainSafe/forest/pull/6325) Fixed a panic that could occur under certain message pool conditions and the `Filecoin.MpoolSelect` RPC method.
5757

58+
- [#5979](https://github.com/ChainSafe/forest/issues/5979) Fixed an issue with `Filecoin.EthGetCode` and `Filecoin.EthGetStorageAt` returning parent tipset data instead of the requested tipset.
59+
60+
- [#6118](https://github.com/ChainSafe/forest/pull/6118) Fixed the `Filecoin.EthGetTransactionReceipt` and `Filecoin.EthGetTransactionReceiptLimited` RPC methods to return null for non-existent transactions instead of an error. This aligns with the Ethereum RPC API provided by Lotus.
61+
62+
- [#6118](https://github.com/ChainSafe/forest/pull/6118) Removed a legacy limit of 100M gas for messages which was preventing contract deployments.
63+
5864
## Forest v0.30.4 "DeLorean"
5965

6066
This is a non-mandatory release that fixes a chain sync issue that is caused by time traveling block(s).

src/message/signed_message.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ impl SignedMessage {
9898
};
9999
Ok(serialized.len())
100100
}
101+
102+
/// Creates a mock signed message for testing purposes. The signature check will fail if
103+
/// invoked.
104+
#[cfg(test)]
105+
pub fn mock_bls_signed_message(message: Message) -> SignedMessage {
106+
let signature = Signature::new_bls(vec![0; crate::shim::crypto::BLS_SIG_LEN]);
107+
SignedMessage::new_unchecked(message, signature)
108+
}
101109
}
102110

103111
impl MessageTrait for SignedMessage {

src/message_pool/msgpool/msg_pool.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -603,12 +603,6 @@ where
603603
bls_sig_cache.push(msg.cid().into(), msg.signature().clone());
604604
}
605605

606-
if msg.message().gas_limit > 100_000_000 {
607-
return Err(Error::Other(
608-
"given message has too high of a gas limit".to_string(),
609-
));
610-
}
611-
612606
api.put_message(&ChainMessage::Signed(msg.clone()))?;
613607
api.put_message(&ChainMessage::Unsigned(msg.message().clone()))?;
614608

@@ -682,3 +676,28 @@ pub fn remove(
682676

683677
Ok(())
684678
}
679+
680+
#[cfg(test)]
681+
mod tests {
682+
use crate::message_pool::test_provider::TestApi;
683+
684+
use super::*;
685+
use crate::shim::message::Message as ShimMessage;
686+
687+
// Regression test for https://github.com/ChainSafe/forest/pull/6118 which fixed a bogus 100M
688+
// gas limit. There are no limits on a single message.
689+
#[test]
690+
fn add_helper_message_gas_limit_test() {
691+
let api = TestApi::default();
692+
let bls_sig_cache = SizeTrackingLruCache::new_mocked();
693+
let pending = SyncRwLock::new(HashMap::new());
694+
let message = ShimMessage {
695+
gas_limit: 666_666_666,
696+
..ShimMessage::default()
697+
};
698+
let msg = SignedMessage::mock_bls_signed_message(message);
699+
let sequence = msg.message().sequence;
700+
let res = add_helper(&api, &bls_sig_cache, &pending, msg, sequence);
701+
assert!(res.is_ok());
702+
}
703+
}

src/rpc/methods/eth.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2157,9 +2157,10 @@ impl RpcMethod<2> for EthGetCode {
21572157
..Default::default()
21582158
};
21592159

2160+
let (state, _) = ctx.state_manager.tipset_state(&ts).await?;
21602161
let api_invoc_result = 'invoc: {
21612162
for ts in ts.chain(ctx.store()) {
2162-
match ctx.state_manager.call(&message, Some(ts)) {
2163+
match ctx.state_manager.call_on_state(state, &message, Some(ts)) {
21632164
Ok(res) => {
21642165
break 'invoc res;
21652166
}
@@ -2208,10 +2209,8 @@ impl RpcMethod<3> for EthGetStorageAt {
22082209
ResolveNullTipset::TakeOlder,
22092210
)?;
22102211
let to_address = FilecoinAddress::try_from(&eth_address)?;
2211-
let Some(actor) = ctx
2212-
.state_manager
2213-
.get_actor(&to_address, *ts.parent_state())?
2214-
else {
2212+
let (state, _) = ctx.state_manager.tipset_state(&ts).await?;
2213+
let Some(actor) = ctx.state_manager.get_actor(&to_address, state)? else {
22152214
return Ok(make_empty_result());
22162215
};
22172216

@@ -2230,7 +2229,7 @@ impl RpcMethod<3> for EthGetStorageAt {
22302229
};
22312230
let api_invoc_result = 'invoc: {
22322231
for ts in ts.chain(ctx.store()) {
2233-
match ctx.state_manager.call(&message, Some(ts)) {
2232+
match ctx.state_manager.call_on_state(state, &message, Some(ts)) {
22342233
Ok(res) => {
22352234
break 'invoc res;
22362235
}
@@ -2799,7 +2798,7 @@ async fn get_eth_transaction_receipt(
27992798
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
28002799
tx_hash: EthHash,
28012800
limit: Option<ChainEpoch>,
2802-
) -> Result<EthTxReceipt, ServerError> {
2801+
) -> Result<Option<EthTxReceipt>, ServerError> {
28032802
let msg_cid = ctx.chain_store().get_mapping(&tx_hash)?.unwrap_or_else(|| {
28042803
tracing::debug!(
28052804
"could not find transaction hash {} in Ethereum mapping",
@@ -2813,7 +2812,16 @@ async fn get_eth_transaction_receipt(
28132812
.state_manager
28142813
.search_for_message(None, msg_cid, limit, Some(true))
28152814
.await
2816-
.with_context(|| format!("failed to lookup Eth Txn {tx_hash} as {msg_cid}"))?;
2815+
.with_context(|| format!("failed to lookup Eth Txn {tx_hash} as {msg_cid}"));
2816+
2817+
let option = match option {
2818+
Ok(opt) => opt,
2819+
// Ethereum clients expect an empty response when the message was not found
2820+
Err(e) => {
2821+
tracing::debug!("could not find transaction receipt for hash {tx_hash}: {e}");
2822+
return Ok(None);
2823+
}
2824+
};
28172825

28182826
let (tipset, receipt) = option.context("not indexed")?;
28192827
let ipld = receipt.return_data().deserialize().unwrap_or(Ipld::Null);
@@ -2846,7 +2854,7 @@ async fn get_eth_transaction_receipt(
28462854

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

2849-
Ok(tx_receipt)
2857+
Ok(Some(tx_receipt))
28502858
}
28512859

28522860
pub enum EthGetTransactionReceipt {}
@@ -2858,7 +2866,7 @@ impl RpcMethod<1> for EthGetTransactionReceipt {
28582866
const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all_with_v2();
28592867
const PERMISSION: Permission = Permission::Read;
28602868
type Params = (EthHash,);
2861-
type Ok = EthTxReceipt;
2869+
type Ok = Option<EthTxReceipt>;
28622870
async fn handle(
28632871
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
28642872
(tx_hash,): Self::Params,
@@ -2876,7 +2884,7 @@ impl RpcMethod<2> for EthGetTransactionReceiptLimited {
28762884
const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all_with_v2();
28772885
const PERMISSION: Permission = Permission::Read;
28782886
type Params = (EthHash, ChainEpoch);
2879-
type Ok = EthTxReceipt;
2887+
type Ok = Option<EthTxReceipt>;
28802888
async fn handle(
28812889
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
28822890
(tx_hash, limit): Self::Params,

src/rpc/methods/miner.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ use enumflags2::BitFlags;
2626
use crate::shim::sector::PoStProof;
2727
use crate::utils::db::CborStoreExt;
2828

29+
use crate::shim::crypto::BLS_SIG_LEN;
2930
use anyhow::{Context as _, Result};
3031
use bls_signatures::Serialize as _;
3132
use cid::Cid;
3233
use fil_actors_shared::fvm_ipld_amt::Amtv0 as Amt;
3334
use fvm_ipld_blockstore::Blockstore;
3435
use fvm_ipld_encoding::tuple::*;
35-
use fvm_shared2::crypto::signature::BLS_SIG_LEN;
3636
use group::prime::PrimeCurveAffine as _;
3737
use itertools::Itertools;
3838
use parking_lot::RwLock;

src/shim/crypto.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use fvm_ipld_encoding::{
1414
repr::{Deserialize_repr, Serialize_repr},
1515
ser, strict_bytes,
1616
};
17+
pub use fvm_shared_latest::crypto::signature::BLS_SIG_LEN;
1718
pub use fvm_shared3::TICKET_RANDOMNESS_LOOKBACK;
1819
use get_size2::GetSize;
1920
use num::FromPrimitive;

src/state_manager/mod.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -582,13 +582,14 @@ where
582582
#[instrument(skip(self, rand))]
583583
fn call_raw(
584584
&self,
585+
state_cid: Option<Cid>,
585586
msg: &Message,
586587
rand: ChainRand<DB>,
587588
tipset: &Tipset,
588589
) -> Result<ApiInvocResult, Error> {
589590
let mut msg = msg.clone();
590591

591-
let state_cid = tipset.parent_state();
592+
let state_cid = state_cid.unwrap_or(*tipset.parent_state());
592593

593594
let tipset_messages = self
594595
.chain_store()
@@ -606,14 +607,14 @@ where
606607
let mut vm = VM::new(
607608
ExecutionContext {
608609
heaviest_tipset: tipset.clone(),
609-
state_tree_root: *state_cid,
610+
state_tree_root: state_cid,
610611
epoch: height,
611612
rand: Box::new(rand),
612613
base_fee: tipset.block_headers().first().parent_base_fee.clone(),
613614
circ_supply: genesis_info.get_vm_circulating_supply(
614615
height,
615616
self.blockstore(),
616-
state_cid,
617+
&state_cid,
617618
)?,
618619
chain_config: self.chain_config().clone(),
619620
chain_index: self.chain_index().clone(),
@@ -660,7 +661,20 @@ where
660661
pub fn call(&self, message: &Message, tipset: Option<Tipset>) -> Result<ApiInvocResult, Error> {
661662
let ts = tipset.unwrap_or_else(|| self.heaviest_tipset());
662663
let chain_rand = self.chain_rand(ts.clone());
663-
self.call_raw(message, chain_rand, &ts)
664+
self.call_raw(None, message, chain_rand, &ts)
665+
}
666+
667+
/// Same as [`StateManager::call`] but runs the message on the given state and not
668+
/// on the parent state of the tipset.
669+
pub fn call_on_state(
670+
&self,
671+
state_cid: Cid,
672+
message: &Message,
673+
tipset: Option<Tipset>,
674+
) -> Result<ApiInvocResult, Error> {
675+
let ts = tipset.unwrap_or_else(|| self.cs.heaviest_tipset());
676+
let chain_rand = self.chain_rand(ts.clone());
677+
self.call_raw(Some(state_cid), message, chain_rand, &ts)
664678
}
665679

666680
pub async fn apply_on_state_with_gas(

src/tool/subcommands/api_cmd/api_compare_tests.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,6 +1983,17 @@ fn eth_tests_with_tipset<DB: Blockstore>(store: &Arc<DB>, shared_tipset: &Tipset
19831983
// both nodes could fail on, e.g., "too many results, maximum supported is 500, try paginating
19841984
// requests with After and Count"
19851985
.policy_on_rejected(PolicyOnRejected::PassWithIdenticalError),
1986+
RpcTest::identity(
1987+
EthGetTransactionReceipt::request((
1988+
// A transaction that should not exist, to test the `null` response in case
1989+
// of missing transaction.
1990+
EthHash::from_str(
1991+
"0xf234567890123456789d6a7b8c9d0e1f2a3b4c5d6e7f8091a2b3c4d5e6f70809",
1992+
)
1993+
.unwrap(),
1994+
))
1995+
.unwrap(),
1996+
),
19861997
];
19871998

19881999
for block in shared_tipset.block_headers() {

src/tool/subcommands/api_cmd/test_snapshots.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,22 @@ filecoin_ethgetblockreceipts_1740132537907751.rpcsnap.json.zst
6161
filecoin_ethgetblockreceiptslimited_1740132537908421.rpcsnap.json.zst
6262
filecoin_ethgetblocktransactioncountbyhash_1740132538001911.rpcsnap.json.zst
6363
filecoin_ethgetblocktransactioncountbynumber_1737446676697272.rpcsnap.json.zst
64-
filecoin_ethgetcode_1737446676697285.rpcsnap.json.zst
64+
filecoin_ethgetcode_1765803672602510.rpcsnap.json.zst # latest
65+
filecoin_ethgetcode_1765803672604518.rpcsnap.json.zst # concrete
66+
filecoin_ethgetcode_1765803672655291.rpcsnap.json.zst # pending
6567
filecoin_ethgetlogs_1759922913569082.rpcsnap.json.zst
6668
filecoin_ethgetmessagecidbytransactionhash_1737446676697418.rpcsnap.json.zst
67-
filecoin_ethgetstorageat_1737446676697795.rpcsnap.json.zst
69+
filecoin_ethgetstorageat_1765803742043605.rpcsnap.json.zst # latest
70+
filecoin_ethgetstorageat_1765803742046844.rpcsnap.json.zst # concrete
71+
filecoin_ethgetstorageat_1765803742145789.rpcsnap.json.zst # pending
6872
filecoin_ethgettransactionbyblockhashandindex_1740132538373654.rpcsnap.json.zst
6973
filecoin_ethgettransactionbyblocknumberandindex_1740132538304408.rpcsnap.json.zst
7074
filecoin_ethgettransactionbyhash_1741272955520821.rpcsnap.json.zst
7175
filecoin_ethgettransactionbyhashlimited_1741272955509708.rpcsnap.json.zst
7276
filecoin_ethgettransactioncount_1740132538183426.rpcsnap.json.zst
7377
filecoin_ethgettransactionhashbycid_1737446676698540.rpcsnap.json.zst
7478
filecoin_ethgettransactionreceipt_1741272955712904.rpcsnap.json.zst
79+
filecoin_ethgettransactionreceipt_1765811578590165.rpcsnap.json.zst # transaction not found
7580
filecoin_ethgettransactionreceiptlimited_1741272955611272.rpcsnap.json.zst
7681
filecoin_ethmaxpriorityfeepergas_1758727346451988.rpcsnap.json.zst
7782
filecoin_ethnewblockfilter_1741779995902203.rpcsnap.json.zst

src/utils/cache/lru.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ where
149149
}
150150
size
151151
}
152+
153+
#[cfg(test)]
154+
pub(crate) fn new_mocked() -> Self {
155+
Self::new_inner(Cow::Borrowed("mocked_cache"), NonZeroUsize::new(1))
156+
}
152157
}
153158

154159
impl<K, V> Collector for SizeTrackingLruCache<K, V>

0 commit comments

Comments
 (0)