diff --git a/category/execution/ethereum/db/test/test_db.cpp b/category/execution/ethereum/db/test/test_db.cpp index 4a552e55f9..6968cf5c6b 100644 --- a/category/execution/ethereum/db/test/test_db.cpp +++ b/category/execution/ethereum/db/test/test_db.cpp @@ -648,7 +648,7 @@ TYPED_TEST( EXPECT_EQ(page.value()[compute_slot_offset(adjacent_key)], value2); } -TEST_F(OnDiskTrieDbWithFileFixture, namespace_reads) +TEST_F(OnDiskTrieDbWithFileFixture, namespace_reads_and_merge) { constexpr uint64_t namespace_1{0x1111111111111111ULL}; constexpr uint64_t namespace_2{0x2222222222222222ULL}; @@ -697,10 +697,13 @@ TEST_F(OnDiskTrieDbWithFileFixture, namespace_reads) State state_1{block_state, Incarnation{1, 1}, false, namespace_1}; State state_2{block_state, Incarnation{1, 2}, false, namespace_2}; + State stale_state_1{block_state, Incarnation{1, 3}, false, namespace_1}; EXPECT_EQ(state_1.get_balance(ADDR_A), account_1.balance); EXPECT_EQ(state_1.get_storage(ADDR_A, key1), value1); EXPECT_EQ(state_2.get_balance(ADDR_A), account_2.balance); EXPECT_EQ(state_2.get_storage(ADDR_A, key1), value2); + EXPECT_EQ(stale_state_1.get_balance(ADDR_A), account_1.balance); + EXPECT_EQ(stale_state_1.get_storage(ADDR_A, key1), value1); mpt::RODb rodb{mpt::ReadOnlyOnDiskDbConfig{.dbname_paths = {this->dbname}}}; TrieRODb trie_ro{rodb}; @@ -713,6 +716,40 @@ TEST_F(OnDiskTrieDbWithFileFixture, namespace_reads) EXPECT_EQ( trie_ro.read_storage(ADDR_A, Incarnation{0, 0}, key1, namespace_2), value2); + + state_1.add_to_balance(ADDR_A, 10); + EXPECT_EQ(state_1.set_storage(ADDR_A, key1, value2), EVMC_STORAGE_MODIFIED); + EXPECT_TRUE(block_state.can_merge(state_1)); + block_state.merge(state_1); + EXPECT_EQ( + block_state.read_account(ADDR_A, namespace_1)->balance, + account_1.balance + 10); + EXPECT_EQ( + block_state.read_storage(ADDR_A, Incarnation{0, 0}, key1, namespace_1), + value2); + EXPECT_EQ(block_state.read_account(ADDR_A, namespace_2), account_2); + EXPECT_EQ( + block_state.read_storage(ADDR_A, Incarnation{0, 0}, key1, namespace_2), + value2); + EXPECT_EQ(block_state.read_account(ADDR_A), std::nullopt); + EXPECT_FALSE(block_state.can_merge(stale_state_1)); + + state_2.add_to_balance(ADDR_A, 20); + EXPECT_EQ(state_2.set_storage(ADDR_A, key1, value1), EVMC_STORAGE_MODIFIED); + EXPECT_TRUE(block_state.can_merge(state_2)); + block_state.merge(state_2); + EXPECT_EQ( + block_state.read_account(ADDR_A, namespace_2)->balance, + account_2.balance + 20); + EXPECT_EQ( + block_state.read_storage(ADDR_A, Incarnation{0, 0}, key1, namespace_2), + value1); + EXPECT_EQ( + block_state.read_account(ADDR_A, namespace_1)->balance, + account_1.balance + 10); + EXPECT_EQ( + block_state.read_storage(ADDR_A, Incarnation{0, 0}, key1, namespace_1), + value2); } TEST(DBTest, read_only) diff --git a/category/execution/ethereum/state2/block_state.cpp b/category/execution/ethereum/state2/block_state.cpp index 6b4f5a996a..5aebd15b0e 100644 --- a/category/execution/ethereum/state2/block_state.cpp +++ b/category/execution/ethereum/state2/block_state.cpp @@ -174,14 +174,26 @@ vm::SharedVarcode BlockState::read_code(bytes32_t const &code_hash) bool BlockState::can_merge(State &state) const { MONAD_ASSERT(state_); - auto const &original = state.original(); - for (auto &kv : original) { + auto const &ns = state.get_namespace(); + StateDeltas const *deltas = nullptr; + if (ns.has_value()) { + NamespacedStateDeltas::const_accessor ns_it{}; + MONAD_ASSERT(ns_state_.find(ns_it, *ns)); + MONAD_ASSERT(ns_it->second); + deltas = ns_it->second.get(); + } + else { + deltas = state_.get(); + } + + for (auto &kv : state.original()) { Address const &address = kv.first; OriginalAccountState const &account_state = kv.second; auto const &account = account_state.account_; auto const &storage = account_state.storage_; + MONAD_ASSERT(deltas); StateDeltas::const_accessor it{}; - MONAD_ASSERT(state_->find(it, address)); + MONAD_ASSERT(deltas->find(it, address)); if (account != it->second.account.second) { // RELAXED MERGE // try to fix original and current in `state` to match the block @@ -233,13 +245,25 @@ void BlockState::merge(State const &state) code_.emplace(code_hash, it->second->intercode()); // TODO try_emplace } - MONAD_ASSERT(state_); + auto const &ns = state.get_namespace(); + StateDeltas *deltas = nullptr; + if (ns.has_value()) { + NamespacedStateDeltas::accessor ns_it{}; + MONAD_ASSERT(ns_state_.find(ns_it, *ns)); + MONAD_ASSERT(ns_it->second); + deltas = ns_it->second.get(); + } + else { + MONAD_ASSERT(state_); + deltas = state_.get(); + } + for (auto const &[address, stack] : current) { auto const &account_state = stack.recent(); auto const &account = account_state.account_; auto const &storage = account_state.storage_; StateDeltas::accessor it{}; - MONAD_ASSERT(state_->find(it, address)); + MONAD_ASSERT(deltas->find(it, address)); it->second.account.second = account; if (account.has_value()) { for (auto const &[key, value] : storage) { diff --git a/category/execution/ethereum/state3/state.hpp b/category/execution/ethereum/state3/state.hpp index 5e7a81d586..33b2cd9d73 100644 --- a/category/execution/ethereum/state3/state.hpp +++ b/category/execution/ethereum/state3/state.hpp @@ -104,6 +104,11 @@ class State State &operator=(State &&) = delete; State &operator=(State const &) = delete; + std::optional const &get_namespace() const + { + return namespace_; + } + Map const &original() const; Map> const ¤t() const;