Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow start from last stored lib #182

Closed
wants to merge 15 commits into from
65 changes: 61 additions & 4 deletions src/block_conversion_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include <silkworm/core/types/transaction.hpp>
#include <silkworm/core/trie/vector_root.hpp>
#include <silkworm/core/common/endian.hpp>
#include <silkworm/node/db/stages.hpp>
#include <silkworm/node/db/access_layer.hpp>

using sys = sys_plugin;

Expand All @@ -35,6 +37,9 @@ class block_conversion_plugin_impl : std::enable_shared_from_this<block_conversi
block_conversion_plugin_impl()
: evm_blocks_channel(appbase::app().get_channel<channels::evm_blocks>()){}

uint64_t get_evm_lib() {
return evm_lib;
}

uint32_t timestamp_to_evm_block_num(uint64_t timestamp) const {
if (timestamp < bm.value().genesis_timestamp) {
Expand Down Expand Up @@ -277,6 +282,14 @@ class block_conversion_plugin_impl : std::enable_shared_from_this<block_conversi
set_upper_bound(curr, *new_block);

// Calculate last irreversible EVM block
SILK_INFO << "Pruning native block queue according to lib in new block:"
<< "#" << new_block->lib;
SILK_INFO << "Size BEFORE pruning: "
<< native_blocks.size();
if (native_blocks.size() > 0 ) {
SILK_INFO << "Begin block: "
<< "#" << native_blocks.begin()->block_num;
}
std::optional<uint64_t> lib_timestamp;
auto it = std::upper_bound(native_blocks.begin(), native_blocks.end(), new_block->lib, [](uint32_t lib, const auto& nb) { return lib < nb.block_num; });
if(it != native_blocks.begin()) {
Expand All @@ -285,18 +298,57 @@ class block_conversion_plugin_impl : std::enable_shared_from_this<block_conversi
}

if( lib_timestamp ) {
auto evm_lib = timestamp_to_evm_block_num(*lib_timestamp) - 1;

auto local_evm_lib = timestamp_to_evm_block_num(*lib_timestamp) - 1;
SILK_INFO << "Pruning according to EVM LIB: "
<< "#" << local_evm_lib;
// Remove irreversible native blocks
while(timestamp_to_evm_block_num(native_blocks.front().timestamp) < evm_lib) {
while(timestamp_to_evm_block_num(native_blocks.front().timestamp) < local_evm_lib) {
native_blocks.pop_front();
}

// Remove irreversible evm blocks
while(evm_blocks.front().header.number < evm_lib) {
// The block at height evm_lib is actually irreversible as well.
// We want to keep at least one irreversible block in the array to deal with forks.
SILK_INFO << "EVM Block Queue Size BEFORE pruning: "
<< evm_blocks.size();
if (evm_blocks.size() > 0 ) {
SILK_INFO << "EVM Block Queue Begin block: "
<< "#" << evm_blocks.begin()->header.number;
}

while(evm_blocks.front().header.number < local_evm_lib) {
evm_blocks.pop_front();
}
SILK_INFO << "EVM Block Queue Size AFTER pruning: "
<< evm_blocks.size();
// The block at evm_lib should have already been irreversible and inserted.
// So we should be able to recover from it.
//
// There's one extreme case that we cannot recover:
// The block is irreversible but the chain db still have the wrong canonical branch.
// One possible case for this to happen is:
// 1 We are in the wrong branch
// 2 We then restart from an earlier irreverisble block
// 3 Stop in the middle of the catchup process so that lib is written but the canonical branch is not updated yet.
// 4 Restart, the lib recored may not in the canonial chain.
// It should be possible for this to happen since we process 5000 blocks together for irreversible blocks.
//
// In this case, the "lib" block we start from will be a block in the "wrong" canonical chain.
// The first fetched block will then be rejected as it cannot link.
// The only relatively easy way to protect against such case is checking whether the evm_lib is canonical here.
// But the situation is rare and at least we will not recover into a wrong state.
// And we can always use the "ship-start-from-canonical-height" to manually recover.
// So we decide we will not do the check for now.

// record_evm_lib(evm_lib);
evm_lib = local_evm_lib;

SILK_INFO << "Stored EVM LIB: "
<< "#" << evm_lib;

}
SILK_INFO << "Size AFTER pruning: "
<< native_blocks.size();
}
);
}
Expand All @@ -315,6 +367,7 @@ class block_conversion_plugin_impl : std::enable_shared_from_this<block_conversi
channels::native_blocks::channel_type::handle native_blocks_subscription;
std::optional<eosevm::block_mapping> bm;
uint64_t evm_contract_name = 0;
uint64_t evm_lib;
};

block_conversion_plugin::block_conversion_plugin() : my(new block_conversion_plugin_impl()) {}
Expand All @@ -336,3 +389,7 @@ void block_conversion_plugin::plugin_shutdown() {
my->shutdown();
SILK_INFO << "Shutdown block_conversion plugin";
}

uint64_t block_conversion_plugin::get_evm_lib() {
return my->get_evm_lib();
}
1 change: 1 addition & 0 deletions src/block_conversion_plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class block_conversion_plugin : public appbase::plugin<block_conversion_plugin>
void plugin_shutdown();

uint32_t get_block_stride() const;
uint64_t get_evm_lib();

private:
std::unique_ptr<class block_conversion_plugin_impl> my;
Expand Down
2 changes: 2 additions & 0 deletions src/blockchain_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class blockchain_plugin_impl : std::enable_shared_from_this<blockchain_plugin_im
if(!(++block_count % 5000) || !new_block->irreversible) {
exec_engine->verify_chain(new_block->header.hash());
block_count=0;
auto evm_lib = appbase::app().get_plugin<block_conversion_plugin>().get_evm_lib();
appbase::app().get_plugin<engine_plugin>().record_evm_lib(evm_lib);
}
} catch (const mdbx::exception& ex) {
sys::error("evm_blocks_subscription: mdbx::exception, " + std::string(ex.what()));
Expand Down
62 changes: 58 additions & 4 deletions src/engine_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,39 @@ class engine_plugin_impl : std::enable_shared_from_this<engine_plugin_impl> {

std::optional<silkworm::Block> get_canonical_block_at_height(std::optional<uint64_t> height) {
uint64_t target = 0;
SILK_INFO << "Determining effective canonical header.";
if (!height) {
auto lib = get_evm_lib();
auto header = get_head_canonical_header();
if(!header) return {};
target = header->number;
if (lib) {
SILK_INFO << "Stored LIB at: " << "#" << *lib;
target = *lib;
// Make sure we start from number smaller than or equal to head if possible.
// But ignore the case where head is not avaiable
if (header && target > header->number) {
target = header->number;
SILK_INFO << "Canonical header is at lower height, set the target height to: " << "#" << target;
}
}
else {
SILK_INFO << "Stored LIB not available.";
// no lib, might be the first run from an old db.
// Use the old logic.
if (!header) {
SILK_INFO << "Failed to read canonical header";
return {};
}
else {
target = header->number;
SILK_INFO << "Canonical header at: " << "#" << target;
}
}
}
else {
// Do not check canonical header.
// If there's anything wrong in that table, overriding here has some chance to fix it.
// Do not check canonical header or lib.
// If there's anything wrong, overriding here has some chance to fix it.
target = *height;
SILK_INFO << "Command line options set the canonical height as " << "#" << target;
}

silkworm::db::ROTxn txn(db_env);
Expand All @@ -156,6 +180,28 @@ class engine_plugin_impl : std::enable_shared_from_this<engine_plugin_impl> {
return block;
}

void record_evm_lib(uint64_t height) {
SILK_INFO << "Saving EVM LIB " << "#" << height;
try {
silkworm::db::RWTxn txn(db_env);
txn.reopen();
write_runtime_states_u64(txn, height, silkworm::db::RuntimeState::kLibProcessed);
txn.commit_and_stop();
}
catch (const std::exception& e) {
SILK_ERROR << "exception: " << e.what();
}
catch(...) {
SILK_INFO << "Unknown exception";
}
SILK_INFO << "Finished EVM LIB " << "#" << height;
}

std::optional<uint64_t> get_evm_lib() {
silkworm::db::ROTxn txn(db_env);
return read_runtime_states_u64(txn, silkworm::db::RuntimeState::kLibProcessed);
}

std::optional<silkworm::BlockHeader> get_genesis_header() {
silkworm::db::ROTxn txn(db_env);
return silkworm::db::read_canonical_header(txn, 0);
Expand Down Expand Up @@ -224,6 +270,14 @@ std::optional<silkworm::Block> engine_plugin::get_canonical_block_at_height(std:
return my->get_canonical_block_at_height(height);
}

void engine_plugin::record_evm_lib(uint64_t height) {
return my->record_evm_lib(height);
}

std::optional<uint64_t> engine_plugin::get_evm_lib() {
return my->get_evm_lib();
}

std::optional<silkworm::BlockHeader> engine_plugin::get_genesis_header() {
return my->get_genesis_header();
}
Expand Down
2 changes: 2 additions & 0 deletions src/engine_plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class engine_plugin : public appbase::plugin<engine_plugin> {
std::optional<silkworm::BlockHeader> get_head_canonical_header();
std::optional<silkworm::Block> get_canonical_block_at_height(std::optional<uint64_t> height);
std::optional<silkworm::BlockHeader> get_genesis_header();
void record_evm_lib(uint64_t height);
std::optional<uint64_t> get_evm_lib();

private:
std::unique_ptr<class engine_plugin_impl> my;
Expand Down
Loading