Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions category/execution/monad/db/commit_block_migration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,14 @@ void commit_block(
BlockHeader const &header, StateDeltas const &state,
BlockCommitAncillaries const &anc);

// apply f to both dbs that are present
template <typename F>
void for_each_db(Db &db, Db *const secondary_db, F &&f)
{
f(db);
if (secondary_db != nullptr) {
f(*secondary_db);
}
}

MONAD_NAMESPACE_END
60 changes: 22 additions & 38 deletions category/execution/runloop/runloop_monad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,14 +282,11 @@ Result<BlockExecOutput> propose_block(

// Core execution: transaction-level EVM execution that tracks state
// changes but does not commit them
db.set_block_and_prefix(
block.header.number - 1,
is_first_block ? bytes32_t{} : consensus_header.parent_id());
if (secondary_db != nullptr) {
secondary_db->set_block_and_prefix(
for_each_db(db, secondary_db, [&](Db &d) {
d.set_block_and_prefix(
block.header.number - 1,
is_first_block ? bytes32_t{} : consensus_header.parent_id());
}
});
block.header.parent_hash =
to_bytes(keccak256(rlp::encode_block_header(db.read_eth_header())));

Expand Down Expand Up @@ -368,21 +365,14 @@ Result<BlockExecOutput> propose_block(

// Dual-db migration: log both timelines' state roots (slot primary vs
// page secondary) so a divergence is visible per block.
if (secondary_db != nullptr) {
LOG_INFO(
"block={}, block_id={} state_root primary={} secondary={}",
block.header.number,
block_id,
db.state_root(),
secondary_db->state_root());
}
else {
LOG_INFO(
"block={}, block_id={} state_root primary={}",
block.header.number,
block_id,
db.state_root());
}
LOG_INFO(
"block={}, block_id={} state_root primary={}{}",
block.header.number,
block_id,
db.state_root(),
secondary_db != nullptr
? fmt::format(" secondary={}", secondary_db->state_root())
: std::string{});

// Emit the block metrics log line
[[maybe_unused]] auto const block_time =
Expand Down Expand Up @@ -668,11 +658,9 @@ Result<std::pair<uint64_t, uint64_t>> runloop_monad(
auto const &header) -> Result<std::pair<uint64_t, uint64_t>> {
auto const block_time_start = std::chrono::steady_clock::now();

db.update_voted_metadata(header.seqno - 1, header.parent_id());
if (secondary_db != nullptr) {
secondary_db->update_voted_metadata(
header.seqno - 1, header.parent_id());
}
for_each_db(db, secondary_db, [&](Db &d) {
d.update_voted_metadata(header.seqno - 1, header.parent_id());
});
record_block_qc(exec_recorder, header, last_finalized_block_number);

uint64_t const block_number = header.execution_inputs.number;
Expand Down Expand Up @@ -733,10 +721,9 @@ Result<std::pair<uint64_t, uint64_t>> runloop_monad(
BlockExecOutput const exec_output,
record_block_result(exec_recorder, propose_dispatch()));

db.update_proposed_metadata(header.seqno, block_id);
if (secondary_db != nullptr) {
secondary_db->update_proposed_metadata(header.seqno, block_id);
}
for_each_db(db, secondary_db, [&](Db &d) {
d.update_proposed_metadata(header.seqno, block_id);
});

log_tps(
block_number,
Expand All @@ -761,20 +748,17 @@ Result<std::pair<uint64_t, uint64_t>> runloop_monad(
"Processing finalization for block {} with block_id {}",
block,
block_id);
db.finalize(block, block_id);
if (secondary_db != nullptr) {
secondary_db->finalize(block, block_id);
}
for_each_db(
db, secondary_db, [&](Db &d) { d.finalize(block, block_id); });
block_hash_chain.finalize(block_id);
record_block_finalized(exec_recorder, block_id, block);
finalized_block_num = block;

if (!verified_blocks.empty() &&
verified_blocks.back() != mpt::INVALID_BLOCK_NUM) {
db.update_verified_block(verified_blocks.back());
if (secondary_db != nullptr) {
secondary_db->update_verified_block(verified_blocks.back());
}
for_each_db(db, secondary_db, [&](Db &d) {
d.update_verified_block(verified_blocks.back());
});
}
record_block_verified(exec_recorder, verified_blocks);
}
Expand Down
48 changes: 33 additions & 15 deletions category/execution/runloop/runloop_monad_ethblocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ void get_block_with_retry(
template <Traits traits>
requires is_monad_trait_v<traits>
Result<void> process_monad_block(
MonadChain const &chain, Db &db, vm::VM &vm,
MonadChain const &chain, Db &db, Db *const secondary_db, vm::VM &vm,
BlockHashBufferFinalized &block_hash_buffer,
fiber::PriorityPool &priority_pool, Block &block, bytes32_t const &block_id,
bytes32_t const &parent_block_id, bool const enable_tracing,
Expand Down Expand Up @@ -209,12 +209,14 @@ Result<void> process_monad_block(

// Core execution: transaction-level EVM execution that tracks state
// changes but does not commit them
db.set_block_and_prefix(block.header.number - 1, parent_block_id);
for_each_db(db, secondary_db, [&](Db &d) {
d.set_block_and_prefix(block.header.number - 1, parent_block_id);
});
block.header.parent_hash =
to_bytes(keccak256(rlp::encode_block_header(db.read_eth_header())));

BlockMetrics block_metrics;
BlockState block_state(db, vm);
BlockState block_state(db, vm, secondary_db);
record_block_marker_event(exec_recorder, MONAD_EXEC_BLOCK_PERF_EVM_ENTER);
BOOST_OUTCOME_TRY(
auto const receipts,
Expand Down Expand Up @@ -247,7 +249,7 @@ Result<void> process_monad_block(
.call_frames = call_frames,
.ommers = block.ommers,
.withdrawals = block.withdrawals};
commit_block<traits>(db, nullptr, block_id, block.header, *state, anc);
commit_block<traits>(db, secondary_db, block_id, block.header, *state, anc);

[[maybe_unused]] auto const commit_time =
std::chrono::duration_cast<std::chrono::microseconds>(
Expand All @@ -266,14 +268,25 @@ Result<void> process_monad_block(

// Commit prologue: database finalization, computation of the Ethereum
// block hash to append to the circular hash buffer
db.finalize(block.header.number, block_id);
db.update_verified_block(block.header.number);
for_each_db(db, secondary_db, [&](Db &d) {
d.finalize(block.header.number, block_id);
d.update_verified_block(block.header.number);
});
exec_output.eth_block_hash =
to_bytes(keccak256(rlp::encode_block_header(exec_output.eth_header)));
block_hash_buffer.set(
exec_output.eth_header.number, exec_output.eth_block_hash);
(void)record_block_result(exec_recorder, exec_output);

LOG_INFO(
"block={}, block_id={} state_root primary={}{}",
block.header.number,
block_id,
db.state_root(),
secondary_db != nullptr
? fmt::format(" secondary={}", secondary_db->state_root())
: std::string{});

// Emit the block metrics log line
[[maybe_unused]] auto const block_time =
std::chrono::duration_cast<std::chrono::microseconds>(
Expand Down Expand Up @@ -321,7 +334,7 @@ Result<std::pair<uint64_t, uint64_t>> runloop_monad_ethblocks(
fiber::PriorityPool &priority_pool, uint64_t &finalized_block_num,
uint64_t const end_block_num, sig_atomic_t const volatile &stop,
bool const enable_tracing, std::chrono::seconds const block_db_timeout,
ExecutionEventRecorder *const exec_recorder)
ExecutionEventRecorder *const exec_recorder, Db *const secondary_db)
{
uint64_t const batch_size =
end_block_num == std::numeric_limits<uint64_t>::max() ? 1 : 1000;
Expand Down Expand Up @@ -414,21 +427,26 @@ Result<std::pair<uint64_t, uint64_t>> runloop_monad_ethblocks(
// matches the TrieDbImpl<page_encoded> backing `db`). If the
// replay crosses the mip-8 cutoff in either direction, the
// encoding the caller picked at startup no longer matches what
// this block's revision expects.
MONAD_ASSERT_PRINTF(
mip_8_active(rev) == db.is_page_encoded(),
"monad revision %d at block %lu crosses mip-8 cutoff "
"but db was opened with page_encoded=%d",
rev,
block.header.number,
db.is_page_encoded());
// this block's revision expects. With a secondary db both
// encodings are present (commit_block asserts slot primary +
// page secondary), so any revision is fine.
if (secondary_db == nullptr) {
MONAD_ASSERT_PRINTF(
mip_8_active(rev) == db.is_page_encoded(),
"monad revision %d at block %lu crosses mip-8 cutoff "
"but db was opened with page_encoded=%d",
rev,
block.header.number,
db.is_page_encoded());
}

ankerl::unordered_dense::segmented_set<Address> senders_and_authorities;
BOOST_OUTCOME_TRY([&] {
SWITCH_MONAD_TRAITS(
process_monad_block,
chain,
db,
secondary_db,
vm,
block_hash_buffer,
priority_pool,
Expand Down
3 changes: 2 additions & 1 deletion category/execution/runloop/runloop_monad_ethblocks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Result<std::pair<uint64_t, uint64_t>> runloop_monad_ethblocks(
MonadChain const &, std::filesystem::path const &, Db &, vm::VM &,
BlockHashBufferFinalized &, fiber::PriorityPool &, uint64_t &, uint64_t,
sig_atomic_t const volatile &, bool enable_tracing,
std::chrono::seconds block_db_timeout, ExecutionEventRecorder *);
std::chrono::seconds block_db_timeout, ExecutionEventRecorder *,
Db *secondary_db);

MONAD_NAMESPACE_END
35 changes: 20 additions & 15 deletions cmd/monad/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,21 @@ try {
chain_rlp_path);
case CHAIN_CONFIG_MONAD_DEVNET:
case CHAIN_CONFIG_MONAD_TESTNET:
case CHAIN_CONFIG_MONAD_MAINNET:
case CHAIN_CONFIG_MONAD_MAINNET: {
std::optional<mpt::Db> secondary_db;
std::optional<TrieDb> secondary_triedb;
if (raw_db.timeline_active(monad::mpt::timeline_id::secondary)) {
Comment thread
Chen-Yifan marked this conversation as resolved.
secondary_db = raw_db.open_secondary_timeline();
MONAD_ASSERT(secondary_db.has_value());
secondary_triedb.emplace(*secondary_db);
MONAD_ASSERT(
secondary_triedb->is_page_encoded(),
"secondary timeline must be page-encoded");
MONAD_ASSERT(
!db.is_page_encoded(),
"primary must be slot-encoded when the secondary "
"timeline is active");
}
if (as_eth_blocks) {
return runloop_monad_ethblocks(
dynamic_cast<MonadChain const &>(*chain),
Expand All @@ -493,7 +507,9 @@ try {
stop,
trace_calls,
block_db_timeout,
exec_recorder);
exec_recorder,
secondary_triedb.has_value() ? &*secondary_triedb
: nullptr);
}
else {
// TODO: Remove this check once dual-db is deprecated.
Expand All @@ -502,8 +518,7 @@ try {
if (chain_config == CHAIN_CONFIG_MONAD_TESTNET ||
chain_config == CHAIN_CONFIG_MONAD_MAINNET) {
bool const primary_is_page = db.is_page_encoded();
bool const secondary_active = raw_db.timeline_active(
monad::mpt::timeline_id::secondary);
bool const secondary_active = secondary_triedb.has_value();
MONAD_ASSERT_PRINTF(
primary_is_page || secondary_active,
"live monad requires a page-encoded timeline "
Expand All @@ -516,17 +531,6 @@ try {
secondary_active);
}

std::optional<mpt::Db> secondary_db;
std::optional<TrieDb> secondary_triedb;
if (raw_db.timeline_active(
monad::mpt::timeline_id::secondary)) {
secondary_db = raw_db.open_secondary_timeline();
MONAD_ASSERT(secondary_db.has_value());
secondary_triedb.emplace(*secondary_db);
MONAD_ASSERT(
secondary_triedb->is_page_encoded(),
"secondary timeline must be page-encoded");
}
return runloop_monad(
dynamic_cast<MonadChain const &>(*chain),
block_db_path,
Expand All @@ -544,6 +548,7 @@ try {
: nullptr);
}
}
}
MONAD_ABORT_PRINTF("Unsupported chain");
}();

Expand Down
Loading