@@ -703,6 +703,90 @@ TYPED_TEST(MonadTraitsTest, reserve_checks_empty_code_hash)
703703 }
704704}
705705
706+ TYPED_TEST (MonadTraitsTest, reserve_checks_prefunded_init_selfdestruct)
707+ {
708+ using traits = typename TestFixture::Trait;
709+ constexpr Address SENDER{1 };
710+ constexpr Address NEW_CONTRACT{2 };
711+ constexpr Address BENEFICIARY{3 };
712+ constexpr uint64_t BASE_FEE_PER_GAS = 10 ;
713+ auto const to_wei = [](uint64_t mon) {
714+ return uint256_t {mon} * 1000000000000000000ULL ;
715+ };
716+
717+ InMemoryMachine machine;
718+ mpt::Db db{machine};
719+ TrieDb tdb{db};
720+ vm::VM vm;
721+ BlockState bs{tdb, vm};
722+
723+ {
724+ State init_state{bs, Incarnation{0 , 0 }};
725+ init_state.add_to_balance (SENDER, to_wei (20 ));
726+ init_state.add_to_balance (NEW_CONTRACT, to_wei (3 ));
727+ MONAD_ASSERT (bs.can_merge (init_state));
728+ bs.merge (init_state);
729+ }
730+
731+ Transaction const tx{
732+ .max_fee_per_gas = BASE_FEE_PER_GAS,
733+ .gas_limit = 1 ,
734+ .type = TransactionType::legacy,
735+ .max_priority_fee_per_gas = 0 ,
736+ };
737+ uint256_t const gas_cost =
738+ uint256_t {BASE_FEE_PER_GAS} * uint256_t {tx.gas_limit };
739+
740+ ankerl::unordered_dense::segmented_set<Address> const
741+ empty_grandparent_senders_and_authorities;
742+ ankerl::unordered_dense::segmented_set<Address> const
743+ empty_parent_senders_and_authorities;
744+ std::vector<Address> const senders = {SENDER};
745+ std::vector<std::vector<std::optional<Address>>> const authorities = {{}};
746+ ankerl::unordered_dense::segmented_set<Address> senders_and_authorities;
747+ senders_and_authorities.insert (SENDER);
748+ ChainContext<traits> const context{
749+ .grandparent_senders_and_authorities =
750+ empty_grandparent_senders_and_authorities,
751+ .parent_senders_and_authorities = empty_parent_senders_and_authorities,
752+ .senders_and_authorities = senders_and_authorities,
753+ .senders = senders,
754+ .authorities = authorities};
755+
756+ State state{bs, Incarnation{1 , 1 }};
757+ init_reserve_balance_context<traits>(
758+ state, SENDER, tx, BASE_FEE_PER_GAS, 0 , context);
759+ state.subtract_from_balance (SENDER, gas_cost);
760+
761+ // Model constructor-time SELFDESTRUCT at a pre-funded address:
762+ // create the account in current incarnation, then selfdestruct it before
763+ // any runtime code is set.
764+ state.create_contract (NEW_CONTRACT);
765+ auto const [inserted, initial_balance] =
766+ state.selfdestruct <traits>(NEW_CONTRACT, BENEFICIARY);
767+ EXPECT_TRUE (inserted);
768+ EXPECT_EQ (initial_balance, to_wei (3 ));
769+ EXPECT_EQ (state.get_balance (NEW_CONTRACT), 0 );
770+ EXPECT_EQ (state.get_balance (BENEFICIARY), to_wei (3 ));
771+
772+ bool const should_revert = revert_transaction<traits>(
773+ SENDER, tx, BASE_FEE_PER_GAS, 0 , state, context);
774+ bool const should_revert_cached = revert_transaction_cached<traits>(state);
775+
776+ if constexpr (traits::monad_rev () < MONAD_FOUR) {
777+ EXPECT_FALSE (should_revert);
778+ EXPECT_FALSE (should_revert_cached);
779+ }
780+ else if constexpr (traits::monad_rev () >= MONAD_NEXT) {
781+ EXPECT_FALSE (should_revert);
782+ EXPECT_FALSE (should_revert_cached);
783+ }
784+ else {
785+ EXPECT_TRUE (should_revert);
786+ EXPECT_TRUE (should_revert_cached);
787+ }
788+ }
789+
706790TYPED_TEST (MonadTraitsTest, system_transaction_sender_is_authority)
707791{
708792 InMemoryMachine machine;
0 commit comments