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

core, rpcdaemon: add check on NoPreMergeConfig and terminal total difficulty #1949

Merged
merged 23 commits into from
Apr 12, 2024
Merged
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
9 changes: 9 additions & 0 deletions silkworm/core/chain/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ nlohmann::json ChainConfig::to_json() const noexcept {
return ret;
}

[[nodiscard]] bool ChainConfig::valid_pre_merge_config() const noexcept {
const bool has_pre_merge_config{!std::holds_alternative<protocol::NoPreMergeConfig>(rule_set_config)};
const bool has_merge_at_genesis{!terminal_total_difficulty || terminal_total_difficulty == 0};
return has_pre_merge_config || has_merge_at_genesis;
}

std::optional<ChainConfig> ChainConfig::from_json(const nlohmann::json& json) noexcept {
if (json.is_discarded() || !json.contains("chainId") || !json["chainId"].is_number()) {
return std::nullopt;
Expand Down Expand Up @@ -169,6 +175,9 @@ std::optional<ChainConfig> ChainConfig::from_json(const nlohmann::json& json) no
/* Note ! genesis_hash is purposely omitted. It must be loaded from db after the
* effective genesis block has been persisted */

if (!config.valid_pre_merge_config()) {
return std::nullopt;
}
return config;
}

Expand Down
3 changes: 3 additions & 0 deletions silkworm/core/chain/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ struct ChainConfig {
[[nodiscard]] std::vector<BlockTime> distinct_fork_times() const;
[[nodiscard]] std::vector<uint64_t> distinct_fork_points() const;

//! \brief Check invariant on pre-Merge config validity
[[nodiscard]] bool valid_pre_merge_config() const noexcept;

//! \brief Return the JSON representation of this object
[[nodiscard]] nlohmann::json to_json() const noexcept;

Expand Down
6 changes: 3 additions & 3 deletions silkworm/core/chain/config_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,14 @@ TEST_CASE("JSON serialization") {
"istanbulBlock":0,
"berlinBlock":0,
"londonBlock":0,
"terminalTotalDifficulty":"39387012740608862000000",
"mergeNetsplitBlock":10000
"mergeNetsplitBlock":10000,
"terminalTotalDifficulty":"0"
})");

const std::optional<ChainConfig> config{ChainConfig::from_json(merge_test_json)};

REQUIRE(config);
CHECK(config->terminal_total_difficulty == intx::from_string<intx::uint256>("39387012740608862000000"));
CHECK(config->terminal_total_difficulty == intx::from_string<intx::uint256>("0"));
CHECK(config->merge_netsplit_block == 10000);

CHECK(config->to_json() == merge_test_json);
Expand Down
21 changes: 17 additions & 4 deletions silkworm/core/protocol/merge_rule_set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ MergeRuleSet::MergeRuleSet(RuleSetPtr pre_merge_rule_set, const ChainConfig& cha

ValidationResult MergeRuleSet::pre_validate_block_body(const Block& block, const BlockState& state) {
if (block.header.difficulty != 0) {
if (!pre_merge_rule_set_) {
return ValidationResult::kUnknownProtocolRuleSet;
}
return pre_merge_rule_set_->pre_validate_block_body(block, state);
}
return RuleSet::pre_validate_block_body(block, state);
Expand All @@ -57,6 +60,9 @@ ValidationResult MergeRuleSet::validate_block_header(const BlockHeader& header,
if (ttd_reached) {
return ValidationResult::kPoWBlockAfterMerge;
}
if (!pre_merge_rule_set_) {
return ValidationResult::kUnknownProtocolRuleSet;
}
return pre_merge_rule_set_->validate_block_header(header, state, with_future_timestamp_check);
}

Expand All @@ -75,7 +81,9 @@ ValidationResult MergeRuleSet::validate_difficulty_and_seal(const BlockHeader& h
void MergeRuleSet::initialize(EVM& evm) {
const BlockHeader& header{evm.block().header};
if (header.difficulty != 0) {
pre_merge_rule_set_->initialize(evm);
if (pre_merge_rule_set_) {
pre_merge_rule_set_->initialize(evm);
}
return;
}

Expand All @@ -95,7 +103,9 @@ void MergeRuleSet::initialize(EVM& evm) {

void MergeRuleSet::finalize(IntraBlockState& state, const Block& block) {
if (block.header.difficulty != 0) {
pre_merge_rule_set_->finalize(state, block);
if (pre_merge_rule_set_) {
pre_merge_rule_set_->finalize(state, block);
}
return;
}

Expand All @@ -111,21 +121,24 @@ void MergeRuleSet::finalize(IntraBlockState& state, const Block& block) {
}

evmc::address MergeRuleSet::get_beneficiary(const BlockHeader& header) {
if (header.difficulty != 0) {
if (header.difficulty != 0 && pre_merge_rule_set_) {
return pre_merge_rule_set_->get_beneficiary(header);
}
return RuleSet::get_beneficiary(header);
}

ValidationResult MergeRuleSet::validate_ommers(const Block& block, const BlockState& state) {
if (block.header.difficulty != 0) {
if (!pre_merge_rule_set_) {
return ValidationResult::kUnknownProtocolRuleSet;
}
return pre_merge_rule_set_->validate_ommers(block, state);
}
return RuleSet::validate_ommers(block, state);
}

BlockReward MergeRuleSet::compute_reward(const Block& block) {
if (block.header.difficulty != 0) {
if (block.header.difficulty != 0 && pre_merge_rule_set_) {
return pre_merge_rule_set_->compute_reward(block);
}
return RuleSet::compute_reward(block);
Expand Down
2 changes: 2 additions & 0 deletions silkworm/core/protocol/rule_set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ static RuleSetPtr pre_merge_rule_set(const ChainConfig& chain_config) {
}

RuleSetPtr rule_set_factory(const ChainConfig& chain_config) {
SILKWORM_ASSERT(chain_config.valid_pre_merge_config());

RuleSetPtr rule_set{pre_merge_rule_set(chain_config)};
if (chain_config.terminal_total_difficulty) {
rule_set = std::make_unique<MergeRuleSet>(std::move(rule_set), chain_config);
Expand Down
7 changes: 7 additions & 0 deletions silkworm/core/protocol/rule_set_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,30 @@
#include <catch2/catch.hpp>

#include <silkworm/core/common/test_util.hpp>
#include <silkworm/core/protocol/param.hpp>

namespace silkworm::protocol {

TEST_CASE("Rule Set factory") {
RuleSetPtr rule_set;
rule_set = rule_set_factory(kMainnetConfig); // Ethash rule set
CHECK(rule_set);
CHECK(rule_set->compute_reward(Block{}).miner == 0);
rule_set = rule_set_factory(kHoleskyConfig); // Merged from genesis
CHECK(rule_set);
CHECK(rule_set->compute_reward(Block{}).miner == 0);
rule_set = rule_set_factory(kSepoliaConfig); // Ethash rule set
CHECK(rule_set);
CHECK(rule_set->compute_reward(Block{}).miner == 0);
rule_set = rule_set_factory(test::kLondonConfig); // No-proof rule set
CHECK(rule_set);
CHECK(rule_set->compute_reward(Block{}).miner == 2000000000000000000);
rule_set = rule_set_factory(kGoerliConfig); // Clique rule set
CHECK(rule_set);
CHECK(rule_set->compute_reward(Block{}).miner == 0);
rule_set = rule_set_factory(ChainConfig{.rule_set_config = bor::Config{}});
CHECK(rule_set);
CHECK(rule_set->compute_reward(Block{}).miner == 0);
rule_set = rule_set_factory(ChainConfig{.rule_set_config = NoPreMergeConfig{}});
CHECK(!rule_set);
}
Expand Down
98 changes: 33 additions & 65 deletions silkworm/rpc/commands/engine_api_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,64 +104,32 @@ class EngineRpcApi_ForTest : public EngineRpcApi {
using testing::InvokeWithoutArgs;

static silkworm::Bytes kBlockHash(32, '\0');
static silkworm::Bytes kChainConfig{*silkworm::from_hex(
"7b22436861696e4e616d65223a22676f65726c69222c"
"22636861696e4964223a352c"
"22636f6e73656e737573223a22636c69717565222c"
"22686f6d657374656164426c6f636b223a302c"
"2264616f466f726b537570706f7274223a747275652c"
"22656970313530426c6f636b223a302c"
"2265697031353048617368223a22307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c"
"22656970313535426c6f636b223a302c"
"2262797a616e7469756d426c6f636b223a302c"
"22636f6e7374616e74696e6f706c65426c6f636b223a302c"
"2270657465727362757267426c6f636b223a302c"
"22697374616e62756c426c6f636b223a313536313635312c"
"226265726c696e426c6f636b223a343436303634342c"
"226c6f6e646f6e426c6f636b223a353036323630352c"
"227465726d696e616c546f74616c446966666963756c7479223a31303739303030302c"
"227465726d696e616c546f74616c446966666963756c7479506173736564223a747275652c"
"227465726d696e616c426c6f636b48617368223a22307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c"
"22636c69717565223a7b22706572696f64223a31352c"
"2265706f6368223a33303030307d7d")};
static silkworm::Bytes kChainConfigNoTerminalTotalDifficulty{*silkworm::from_hex(
"7b22436861696e4e616d65223a22676f65726c69222c"
"22636861696e4964223a352c"
"22636f6e73656e737573223a22636c69717565222c"
"22686f6d657374656164426c6f636b223a302c"
"2264616f466f726b537570706f7274223a747275652c"
"22656970313530426c6f636b223a302c"
"2265697031353048617368223a22307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c"
"22656970313535426c6f636b223a302c"
"2262797a616e7469756d426c6f636b223a302c"
"22636f6e7374616e74696e6f706c65426c6f636b223a302c"
"2270657465727362757267426c6f636b223a302c"
"22697374616e62756c426c6f636b223a313536313635312c"
"226265726c696e426c6f636b223a343436303634342c"
"226c6f6e646f6e426c6f636b223a353036323630352c"
"227465726d696e616c546f74616c446966666963756c7479506173736564223a747275652c"
"227465726d696e616c426c6f636b48617368223a22307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c"
"22636c69717565223a7b22706572696f64223a31352c"
"2265706f6368223a33303030307d7d")};
static silkworm::Bytes kChainConfigNoTerminalBlockNumber{*silkworm::from_hex(
"7b22436861696e4e616d65223a22676f65726c69222c"
"22636861696e4964223a352c"
"22636f6e73656e737573223a22636c69717565222c"
"22686f6d657374656164426c6f636b223a302c"
"2264616f466f726b537570706f7274223a747275652c"
"22656970313530426c6f636b223a302c"
"2265697031353048617368223a22307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c"
"22656970313535426c6f636b223a302c"
"2262797a616e7469756d426c6f636b223a302c"
"22636f6e7374616e74696e6f706c65426c6f636b223a302c"
"2270657465727362757267426c6f636b223a302c"
"22697374616e62756c426c6f636b223a313536313635312c"
"226265726c696e426c6f636b223a343436303634342c"
"226c6f6e646f6e426c6f636b223a353036323630352c"
"227465726d696e616c546f74616c446966666963756c7479223a31303739303030302c"
"227465726d696e616c546f74616c446966666963756c7479506173736564223a747275652c"
"227465726d696e616c426c6f636b48617368223a223078"
"30303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030227d")};
const silkworm::ChainConfig kChainConfig{
.chain_id = 5,
.homestead_block = 0,
.tangerine_whistle_block = 0,
.spurious_dragon_block = 0,
.byzantium_block = 0,
.constantinople_block = 0,
.petersburg_block = 0,
.istanbul_block = 1561651,
.berlin_block = 4460644,
.london_block = 5062605,
.terminal_total_difficulty = 10790000,
.rule_set_config = protocol::CliqueConfig{}};

const silkworm::ChainConfig kChainConfigNoTerminalTotalDifficulty{
.chain_id = 5,
.homestead_block = 0,
.tangerine_whistle_block = 0,
.spurious_dragon_block = 0,
.byzantium_block = 0,
.constantinople_block = 0,
.petersburg_block = 0,
.istanbul_block = 1561651,
.berlin_block = 4460644,
.london_block = 5062605,
.rule_set_config = protocol::CliqueConfig{}};

using EngineRpcApiTest = test::JsonApiTestBase<EngineRpcApi_ForTest>;

Expand Down Expand Up @@ -739,7 +707,7 @@ TEST_CASE("handle_engine_transition_configuration_v1 succeeds if EL configuratio
const silkworm::Bytes genesis_block_hash{*silkworm::from_hex("0000000000000000000000000000000000000000000000000000000000000000")};
const silkworm::ByteView genesis_block_key{genesis_block_hash};
EXPECT_CALL(*mock_cursor, seek_exact(genesis_block_key)).WillOnce(InvokeWithoutArgs([&]() -> Task<KeyValue> {
co_return KeyValue{silkworm::Bytes{}, kChainConfig};
co_return KeyValue{silkworm::Bytes{}, Bytes{byte_view_of_string(kChainConfig.to_json().dump())}};
}));

std::unique_ptr<ethdb::Database> database_ptr = std::make_unique<DummyDatabase>(mock_cursor);
Expand Down Expand Up @@ -798,7 +766,7 @@ TEST_CASE("handle_engine_transition_configuration_v1 succeeds and default termin
const silkworm::Bytes genesis_block_hash{*silkworm::from_hex("0000000000000000000000000000000000000000000000000000000000000000")};
const silkworm::ByteView genesis_block_key{genesis_block_hash};
EXPECT_CALL(*mock_cursor, seek_exact(genesis_block_key)).WillOnce(InvokeWithoutArgs([&]() -> Task<KeyValue> {
co_return KeyValue{silkworm::Bytes{}, kChainConfigNoTerminalBlockNumber};
co_return KeyValue{silkworm::Bytes{}, Bytes{byte_view_of_string(kChainConfig.to_json().dump())}};
}));

std::unique_ptr<ethdb::Database> database_ptr = std::make_unique<DummyDatabase>(mock_cursor);
Expand Down Expand Up @@ -857,7 +825,7 @@ TEST_CASE("handle_engine_transition_configuration_v1 fails if incorrect terminal
const silkworm::Bytes genesis_block_hash{*silkworm::from_hex("0000000000000000000000000000000000000000000000000000000000000000")};
const silkworm::ByteView genesis_block_key{genesis_block_hash};
EXPECT_CALL(*mock_cursor, seek_exact(genesis_block_key)).WillOnce(InvokeWithoutArgs([&]() -> Task<KeyValue> {
co_return KeyValue{silkworm::Bytes{}, kChainConfig};
co_return KeyValue{silkworm::Bytes{}, Bytes{byte_view_of_string(kChainConfig.to_json().dump())}};
}));

std::unique_ptr<ethdb::Database> database_ptr = std::make_unique<DummyDatabase>(mock_cursor);
Expand Down Expand Up @@ -914,7 +882,7 @@ TEST_CASE("handle_engine_transition_configuration_v1 fails if execution layer do
const silkworm::Bytes genesis_block_hash{*silkworm::from_hex("0000000000000000000000000000000000000000000000000000000000000000")};
const silkworm::ByteView genesis_block_key{genesis_block_hash};
EXPECT_CALL(*mock_cursor, seek_exact(genesis_block_key)).WillOnce(InvokeWithoutArgs([&]() -> Task<KeyValue> {
co_return KeyValue{silkworm::Bytes{}, kChainConfigNoTerminalTotalDifficulty};
co_return KeyValue{silkworm::Bytes{}, Bytes{byte_view_of_string(kChainConfigNoTerminalTotalDifficulty.to_json().dump())}};
}));

std::unique_ptr<ethdb::Database> database_ptr = std::make_unique<DummyDatabase>(mock_cursor);
Expand Down Expand Up @@ -971,7 +939,7 @@ TEST_CASE("handle_engine_transition_configuration_v1 fails if consensus layer se
const silkworm::Bytes genesis_block_hash{*silkworm::from_hex("0000000000000000000000000000000000000000000000000000000000000000")};
const silkworm::ByteView genesis_block_key{genesis_block_hash};
EXPECT_CALL(*mock_cursor, seek_exact(genesis_block_key)).WillOnce(InvokeWithoutArgs([&]() -> Task<KeyValue> {
co_return KeyValue{silkworm::Bytes{}, kChainConfig};
co_return KeyValue{silkworm::Bytes{}, Bytes{byte_view_of_string(kChainConfig.to_json().dump())}};
}));

std::unique_ptr<ethdb::Database> database_ptr = std::make_unique<DummyDatabase>(mock_cursor);
Expand Down Expand Up @@ -1028,7 +996,7 @@ TEST_CASE("handle_engine_transition_configuration_v1 fails if consensus layer se
const silkworm::Bytes genesis_block_hash{*silkworm::from_hex("0000000000000000000000000000000000000000000000000000000000000000")};
const silkworm::ByteView genesis_block_key{genesis_block_hash};
EXPECT_CALL(*mock_cursor, seek_exact(genesis_block_key)).WillOnce(InvokeWithoutArgs([&]() -> Task<KeyValue> {
co_return KeyValue{silkworm::Bytes{}, kChainConfig};
co_return KeyValue{silkworm::Bytes{}, Bytes{byte_view_of_string(kChainConfig.to_json().dump())}};
}));

std::unique_ptr<ethdb::Database> database_ptr = std::make_unique<DummyDatabase>(mock_cursor);
Expand Down Expand Up @@ -1085,7 +1053,7 @@ TEST_CASE("handle_engine_transition_configuration_v1 succeeds w/o matching termi
const silkworm::Bytes genesis_block_hash{*silkworm::from_hex("0000000000000000000000000000000000000000000000000000000000000000")};
const silkworm::ByteView genesis_block_key{genesis_block_hash};
EXPECT_CALL(*mock_cursor, seek_exact(genesis_block_key)).WillOnce(InvokeWithoutArgs([&]() -> Task<KeyValue> {
co_return KeyValue{silkworm::Bytes{}, kChainConfig};
co_return KeyValue{silkworm::Bytes{}, Bytes{byte_view_of_string(kChainConfig.to_json().dump())}};
}));

std::unique_ptr<ethdb::Database> database_ptr = std::make_unique<DummyDatabase>(mock_cursor);
Expand Down
7 changes: 7 additions & 0 deletions silkworm/rpc/daemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ int Daemon::run(const DaemonSettings& settings, const DaemonInfo& info) {
snapshot_repository->reopen_folder();

db::DataModel::set_snapshot_repository(snapshot_repository.get());

// At startup check that chain configuration is valid
db::ROTxnManaged ro_txn{*chaindata_env};
db::DataModel data_access{ro_txn};
if (const auto chain_config{data_access.read_chain_config()}; !chain_config) {
throw std::runtime_error{"invalid chain configuration"};
}
}

// Create the one-and-only Silkrpc daemon
Expand Down
Loading