@@ -3704,3 +3704,118 @@ TEST_F(EthCallFixture, eth_call_reserve_balance_emptying)
37043704 monad_executor_destroy (executor);
37053705 monad_state_override_destroy (state_override);
37063706}
3707+
3708+ // Check that gas < reserve assertion in reserve balance implementation doesn't
3709+ // crash the RPC process
3710+ TEST_F (EthCallFixture, eth_call_reserve_balance_assertion)
3711+ {
3712+ for (uint64_t i = 0 ; i < 256 ; ++i) {
3713+ commit_sequential (tdb, {}, {}, BlockHeader{.number = i});
3714+ }
3715+
3716+ static constexpr auto sender =
3717+ 0x0000000000000000000000000000000011111111_address;
3718+
3719+ static constexpr auto contract =
3720+ 0x0000000000000000000000000000000022222222_address;
3721+
3722+ static constexpr auto recipient =
3723+ 0x0000000000000000000000000000000044444444_address;
3724+
3725+ // No-op delegation target
3726+ auto const contract_code = 0x00_bytes;
3727+ auto const contract_code_hash = to_bytes (keccak256 (contract_code));
3728+ auto const contract_icode = monad::vm::make_shared_intercode (contract_code);
3729+
3730+ // Delegate to contract
3731+ auto const delegated_eoa_code =
3732+ 0xef01000000000000000000000000000000000022222222_bytes;
3733+ auto const delegated_eoa_code_hash =
3734+ to_bytes (keccak256 (delegated_eoa_code));
3735+ auto const delegated_eoa_icode =
3736+ monad::vm::make_shared_intercode (delegated_eoa_code);
3737+
3738+ EXPECT_TRUE (vm::evm::is_delegated (delegated_eoa_code));
3739+
3740+ BlockHeader const header{
3741+ .number = 256 ,
3742+ .base_fee_per_gas = 100'000'000'000 ,
3743+ };
3744+
3745+ commit_sequential (
3746+ tdb,
3747+ StateDeltas{
3748+ {sender,
3749+ StateDelta{
3750+ .account =
3751+ {std::nullopt ,
3752+ Account{
3753+ .balance = uint256_t {1'000'000'000'000'000'000 } * 12 ,
3754+ .code_hash = delegated_eoa_code_hash,
3755+ .nonce = 0 }}}},
3756+ {contract,
3757+ StateDelta{
3758+ .account =
3759+ {std::nullopt ,
3760+ Account{
3761+ .balance = 0 ,
3762+ .code_hash = contract_code_hash,
3763+ .nonce = 0 }}}},
3764+ {recipient,
3765+ StateDelta{
3766+ .account =
3767+ {std::nullopt ,
3768+ Account{
3769+ .balance = 0 , .code_hash = NULL_HASH, .nonce = 0 }}}},
3770+ },
3771+ Code{
3772+ {delegated_eoa_code_hash, delegated_eoa_icode},
3773+ {contract_code_hash, contract_icode},
3774+ },
3775+ header);
3776+
3777+ Transaction const tx{
3778+ .max_fee_per_gas = 100'000'000'000'001 ,
3779+ .gas_limit = 100'000u ,
3780+ .to = recipient,
3781+ };
3782+
3783+ auto const rlp_tx = to_vec (rlp::encode_transaction (tx));
3784+ auto const rlp_header = to_vec (rlp::encode_block_header (header));
3785+ auto const rlp_sender =
3786+ to_vec (rlp::encode_address (std::make_optional (sender)));
3787+ auto const rlp_block_id = to_vec (rlp_finalized_id);
3788+
3789+ auto *executor = create_executor (dbname.string ());
3790+ auto *state_override = monad_state_override_create ();
3791+
3792+ struct callback_context ctx;
3793+ boost::fibers::future<void > f = ctx.promise .get_future ();
3794+
3795+ monad_executor_eth_call_submit (
3796+ executor,
3797+ CHAIN_CONFIG_MONAD_DEVNET,
3798+ rlp_tx.data (),
3799+ rlp_tx.size (),
3800+ rlp_header.data (),
3801+ rlp_header.size (),
3802+ rlp_sender.data (),
3803+ rlp_sender.size (),
3804+ header.number ,
3805+ rlp_block_id.data (),
3806+ rlp_block_id.size (),
3807+ state_override,
3808+ complete_callback,
3809+ (void *)&ctx,
3810+ NOOP_TRACER,
3811+ true );
3812+ f.get ();
3813+
3814+ EXPECT_EQ (ctx.result ->status_code , EVMC_INTERNAL_ERROR);
3815+ EXPECT_EQ (
3816+ std::string_view{ctx.result ->message },
3817+ " gas fee greater than reserve for non-dipping transaction" );
3818+
3819+ monad_executor_destroy (executor);
3820+ monad_state_override_destroy (state_override);
3821+ }
0 commit comments