Skip to content

Commit dfd5207

Browse files
Baltolijhunsaker
authored andcommitted
Patch RPC dos
1 parent 07bbf1c commit dfd5207

File tree

2 files changed

+123
-1
lines changed

2 files changed

+123
-1
lines changed

category/execution/monad/reserve_balance.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include <category/core/assert.h>
1717
#include <category/core/config.hpp>
18+
#include <category/core/monad_exception.hpp>
1819
#include <category/execution/ethereum/core/transaction.hpp>
1920
#include <category/execution/ethereum/state3/state.hpp>
2021
#include <category/execution/ethereum/transaction_gas.hpp>
@@ -93,7 +94,9 @@ bool dipped_into_reserve(
9394
if (addr == sender) {
9495
if (!can_sender_dip_into_reserve(
9596
sender, i, effective_is_delegated, ctx)) {
96-
MONAD_ASSERT(
97+
// Safety: this assertion is recoverable because it can be
98+
// triggered via RPC parameter setting.
99+
MONAD_ASSERT_THROW(
97100
violation_threshold.has_value(),
98101
"gas fee greater than reserve for non-dipping "
99102
"transaction");
@@ -102,6 +105,10 @@ bool dipped_into_reserve(
102105
// Skip if allowed to dip into reserve
103106
}
104107
else {
108+
// Safety: this assertion should not be a recoverable one, as it
109+
// indicates a logic error in the surrounding code: the
110+
// violation threshold can only be nullopt when addr == sender,
111+
// which is not the case in this branch.
105112
MONAD_ASSERT(violation_threshold.has_value());
106113
return true;
107114
}

category/rpc/monad_executor_test.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)