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

Consensus parameters storage #124

Merged
merged 9 commits into from
Feb 29, 2024
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ endif()

# Silkworm itself
add_subdirectory(silkworm)
add_subdirectory(eosevm)

if(NOT SILKWORM_HAS_PARENT)
add_subdirectory(cmd)
Expand Down
35 changes: 35 additions & 0 deletions eosevm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

find_package(Microsoft.GSL REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(tl-expected REQUIRED)

if(MSVC)
add_compile_options(/EHsc)
else()
add_compile_options(-fno-exceptions)
endif()

file(
GLOB_RECURSE
EOS_EVM_SRC
CONFIGURE_DEPENDS
"*.cpp"
"*.hpp"
"*.c"
"*.h"
)
list(FILTER EOS_EVM_SRC EXCLUDE REGEX "_test\\.cpp$")
list(FILTER EOS_EVM_SRC EXCLUDE REGEX "_benchmark\\.cpp$")

add_library(eos_evm ${EOS_EVM_SRC})
target_include_directories(eos_evm PUBLIC ${SILKWORM_MAIN_DIR})

set(EOS_EVM_PUBLIC_LIBS
intx::intx
nlohmann_json::nlohmann_json
)

target_link_libraries(
eos_evm
PUBLIC ${EOS_EVM_PUBLIC_LIBS}
)
6 changes: 6 additions & 0 deletions eosevm/consensus_parameters.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "consensus_parameters.hpp"

namespace eosevm {
bool operator==(const eosevm::GasFeeParameters& a, const eosevm::GasFeeParameters& b) { return a.to_json() == b.to_json(); }
elmato marked this conversation as resolved.
Show resolved Hide resolved
bool operator==(const eosevm::ConsensusParameters& a, const eosevm::ConsensusParameters& b) { return a.to_json() == b.to_json(); }
elmato marked this conversation as resolved.
Show resolved Hide resolved
} // namespace eosevm
110 changes: 110 additions & 0 deletions eosevm/consensus_parameters.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#pragma once

#include <cstdint>
#include <optional>
#include <string>

#include <intx/intx.hpp>

#if not defined(ANTELOPE)
#include <nlohmann/json.hpp>
#endif


namespace eosevm {

struct GasFeeParameters {
// gas_txnewaccount = account_bytes * gas_per_byte
uint64_t gas_txnewaccount;
// gas_newaccount = account_bytes * gas_per_byte
uint64_t gas_newaccount;
// gas_txcreate = gas_create = contract_fixed_bytes * gas_per_byte
uint64_t gas_txcreate;
// gas_codedeposit = gas_per_byte
uint64_t gas_codedeposit;
// gas_sset = 100 + storage_slot_bytes * gas_per_byte
uint64_t gas_sset;

#if not defined(ANTELOPE)
[[nodiscard]] nlohmann::json to_json() const noexcept {
nlohmann::json ret;
ret["gasTxnewaccount"] = gas_txnewaccount;
ret["gasNewaccount"] = gas_newaccount;
ret["gasTxcreate"] = gas_txcreate;
ret["gasCodedeposit"] = gas_codedeposit;
ret["gasSset"] = gas_sset;

return ret;
}
#endif

//! \brief Try parse a JSON object into strongly typed ChainConfig
//! \remark Should this return std::nullopt the parsing has failed
#if not defined(ANTELOPE)

static std::optional<GasFeeParameters> from_json(const nlohmann::json& json) noexcept {
GasFeeParameters feeParams;

if (!json.contains("gasTxnewaccount") || !json.contains("gasNewaccount") || !json.contains("gasTxcreate") ||
!json.contains("gasCodedeposit") || !json.contains("gasSset")) {
// Faii if any of the parameters are missing.
return std::nullopt;
}

feeParams.gas_txnewaccount = json["gasTxnewaccount"].get<uint64_t>();
feeParams.gas_newaccount = json["gasNewaccount"].get<uint64_t>();
feeParams.gas_txcreate = json["gasTxcreate"].get<uint64_t>();
feeParams.gas_codedeposit = json["gasCodedeposit"].get<uint64_t>();
feeParams.gas_sset = json["gasSset"].get<uint64_t>();

return feeParams;
}

#endif

friend bool operator==(const GasFeeParameters&, const GasFeeParameters&);
};

struct ConsensusParameters {
std::optional<intx::uint256> min_gas_price;
elmato marked this conversation as resolved.
Show resolved Hide resolved
std::optional<GasFeeParameters> gas_fee_parameters;

//! \brief Return the JSON representation of this object
#if not defined(ANTELOPE)
[[nodiscard]] nlohmann::json to_json() const noexcept {

nlohmann::json ret;
if (min_gas_price) {
ret["minGasPrice"] = intx::to_string(min_gas_price.value());
}
if (gas_fee_parameters) {
ret["gasFeeParameters"] = gas_fee_parameters.value().to_json();
}

return ret;
};
#endif

//! \brief Try parse a JSON object into strongly typed ChainConfig
//! \remark Should this return std::nullopt the parsing has failed
#if not defined(ANTELOPE)
static std::optional<ConsensusParameters> from_json(const nlohmann::json& json) noexcept {
ConsensusParameters config{};
if (json.contains("minGasPrice")) {
config.min_gas_price = intx::from_string<intx::uint256>(json["minGasPrice"].get<std::string>());
}

if (json.contains("gasFeeParameters")) {
// Can be nullopt if parsing GasFeeParameters failed.
config.gas_fee_parameters = GasFeeParameters::from_json(json["gasFeeParameters"]);
}

return config;
}
#endif

friend bool operator==(const ConsensusParameters&, const ConsensusParameters&);

};

} // namespace eosevm
17 changes: 16 additions & 1 deletion silkworm/core/types/block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ bool operator==(const BlockHeader& a, const BlockHeader& b) {
a.receipts_root == b.receipts_root && a.logs_bloom == b.logs_bloom && a.difficulty == b.difficulty &&
a.number == b.number && a.gas_limit == b.gas_limit && a.gas_used == b.gas_used &&
a.timestamp == b.timestamp && a.extra_data == b.extra_data && a.prev_randao == b.prev_randao &&
a.nonce == b.nonce && a.base_fee_per_gas == b.base_fee_per_gas;
a.nonce == b.nonce && a.base_fee_per_gas == b.base_fee_per_gas && a.consensus_parameter_index == b.consensus_parameter_index;
}

bool operator==(const BlockBody& a, const BlockBody& b) {
Expand Down Expand Up @@ -110,6 +110,9 @@ namespace rlp {
rlp_head.payload_length += kHashLength + 1; // prev_randao
rlp_head.payload_length += 8 + 1; // nonce
}
if (header.consensus_parameter_index) {
elmato marked this conversation as resolved.
Show resolved Hide resolved
rlp_head.payload_length += length(*header.consensus_parameter_index);
}
if (header.base_fee_per_gas) {
rlp_head.payload_length += length(*header.base_fee_per_gas);
}
Expand Down Expand Up @@ -155,6 +158,9 @@ namespace rlp {
encode(to, header.prev_randao);
encode(to, header.nonce);
}
if (header.consensus_parameter_index) {
elmato marked this conversation as resolved.
Show resolved Hide resolved
encode(to, *header.consensus_parameter_index);
}
if (header.base_fee_per_gas) {
encode(to, *header.base_fee_per_gas);
}
Expand Down Expand Up @@ -202,6 +208,15 @@ namespace rlp {
return res;
}

if (from.length() > leftover) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this to the end after excess_data_gas

to.consensus_parameter_index = 0;
if (DecodingResult res{decode(from, *to.consensus_parameter_index, Leftover::kAllow)}; !res) {
return res;
}
} else {
to.consensus_parameter_index = std::nullopt;
}

if (from.length() > leftover) {
to.base_fee_per_gas = 0;
if (DecodingResult res{decode(from, *to.base_fee_per_gas, Leftover::kAllow)}; !res) {
Expand Down
3 changes: 3 additions & 0 deletions silkworm/core/types/block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ struct BlockHeader {
evmc::bytes32 prev_randao{}; // mix hash prior to EIP-4399
NonceType nonce{};

// EOS-EVM
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this to the end after excess_data_gas

std::optional<uint64_t> consensus_parameter_index{std::nullopt};

std::optional<intx::uint256> base_fee_per_gas{std::nullopt}; // EIP-1559
std::optional<evmc::bytes32> withdrawals_root{std::nullopt}; // EIP-4895

Expand Down
19 changes: 19 additions & 0 deletions silkworm/core/types/block_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,27 @@ TEST_CASE("EIP-2718 Block RLP") {
CHECK(block.transactions[1].access_list.size() == 1);
}

TEST_CASE("Consensus Parameters Header RLP") {
BlockHeader h{
.number = 13'500'000,
.consensus_parameter_index= 1234,
};

Bytes rlp;
rlp::encode(rlp, h);

ByteView view{rlp};
BlockHeader decoded;
REQUIRE(rlp::decode(view, decoded));

CHECK(view.empty());
CHECK(decoded == h);
}

TEST_CASE("EIP-1559 Header RLP") {
BlockHeader h{
.number = 13'500'000,
.consensus_parameter_index= 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be reverted after moving consensus_parameter_index to the end on both encode/decode rlp functions

.base_fee_per_gas = 2'700'000'000,
};

Expand All @@ -194,6 +212,7 @@ TEST_CASE("EIP-4844 Header RLP") {
.ommers_hash = kEmptyListHash,
.number = 17'000'000,
.prev_randao = 0xd01681d2b3acdebff0288a02a1648b3910500961982d5ecdbef064af7c34090b_bytes32,
.consensus_parameter_index= 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

.base_fee_per_gas = 2'700'000'000,
.withdrawals_root = 0xbac9348581b0ee244d6eb61076b63c4e4afa70430c804ab0e6a0ab69d9a9d323_bytes32,
.data_gas_used = 456,
Expand Down
1 change: 1 addition & 0 deletions silkworm/node/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ endif()
target_include_directories(silkworm_node PUBLIC "${SILKWORM_MAIN_DIR}")

set(SILKWORM_NODE_PUBLIC_LIBS
eos_evm
silkworm_core
silkworm_infra
silkworm_sentry
Expand Down
21 changes: 21 additions & 0 deletions silkworm/node/db/access_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1217,4 +1217,25 @@ void write_runtime_states_u64(RWTxn& txn, uint64_t num, RuntimeState runtime_sta
write_runtime_states_bytes(txn, value, runtime_state);
}

std::optional<eosevm::ConsensusParameters> read_consensus_parameters(ROTxn& txn, BlockNum index) {
elmato marked this conversation as resolved.
Show resolved Hide resolved
auto cursor = txn.ro_cursor(table::kConsensusParameters);
auto key{db::block_key(index)};
auto data{cursor->find(to_slice(key), /*throw_notfound=*/false)};
if (!data) {
return std::nullopt;
}

// https://github.com/nlohmann/json/issues/2204
const auto json = nlohmann::json::parse(data.value.as_string(), nullptr, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should decode the rlp encoded version of the struct instead of the json representation

return eosevm::ConsensusParameters::from_json(json);
}

void update_consensus_parameters(RWTxn& txn, BlockNum index, const eosevm::ConsensusParameters& config) {
auto cursor = txn.rw_cursor(table::kConsensusParameters);
auto key{db::block_key(index)};

auto config_data{config.to_json().dump()};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should store the encoded rlp version of the struct

cursor->upsert(to_slice(key), mdbx::slice(config_data.data()));
}

} // namespace silkworm::db
8 changes: 8 additions & 0 deletions silkworm/node/db/access_layer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include <silkworm/node/db/util.hpp>
#include <silkworm/node/snapshot/repository.hpp>

#include <eosevm/consensus_parameters.hpp>

namespace silkworm::db {

//! \brief Pulls database schema version
Expand Down Expand Up @@ -249,6 +251,12 @@ std::optional<uint64_t> read_runtime_states_u64(ROTxn& txn, RuntimeState runtime
//! \brief Write uint64_t as runtime states by index
void write_runtime_states_u64(RWTxn& txn, uint64_t num, RuntimeState runtime_state);

//! \brief Read ConsensusParameters indexed by blocknum that it is added.
std::optional<eosevm::ConsensusParameters> read_consensus_parameters(ROTxn& txn, BlockNum index);

//! \brief Write ConsensusParameters indexed by blocknum that it is added. Can overwrite during forks.
void update_consensus_parameters(RWTxn& txn, BlockNum index, const eosevm::ConsensusParameters& config);

class DataModel {
public:
static void set_snapshot_repository(snapshot::SnapshotRepository* repository);
Expand Down
44 changes: 44 additions & 0 deletions silkworm/node/db/access_layer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -911,4 +911,48 @@ TEST_CASE("RuntimeStates_bytes") {
CHECK(read_runtime_states_bytes(txn, RuntimeState(1)) == value1);
}

TEST_CASE("ConsensusParameters") {
test::Context context;
auto& txn{context.rw_txn()};

constexpr eosevm::ConsensusParameters value1{
.min_gas_price = 1,
.gas_fee_parameters = eosevm::GasFeeParameters{
.gas_txnewaccount = 1,
.gas_newaccount = 1,
.gas_txcreate = 1,
.gas_codedeposit = 1,
.gas_sset = 1
}
};

constexpr eosevm::ConsensusParameters value2{
.min_gas_price = 2,
.gas_fee_parameters = eosevm::GasFeeParameters{
.gas_txnewaccount = 2,
.gas_newaccount = 2,
.gas_txcreate = 2,
.gas_codedeposit = 2,
.gas_sset = 2,
},
};

CHECK(read_consensus_parameters(txn, 0) == std::nullopt);

update_consensus_parameters(txn, 0, value1 );
CHECK(read_consensus_parameters(txn, 0) == value1);

update_consensus_parameters(txn, 0, value2 );
CHECK(read_consensus_parameters(txn, 0) == value2);

CHECK(read_consensus_parameters(txn, 1) == std::nullopt);

update_consensus_parameters(txn, 1, value2 );
CHECK(read_consensus_parameters(txn, 1) == value2);

update_consensus_parameters(txn, 1, value1 );
CHECK(read_consensus_parameters(txn, 1) == value1);
}


} // namespace silkworm::db
4 changes: 4 additions & 0 deletions silkworm/node/db/tables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ inline constexpr db::MapConfig kCumulativeGasIndex{kCumulativeGasIndexName};
inline constexpr const char* kRuntimeStatesName{"RuntimeStates"};
inline constexpr db::MapConfig kRuntimeStates{kRuntimeStatesName};

inline constexpr const char* kConsensusParametersName{"ConsensusParameters"};
inline constexpr db::MapConfig kConsensusParameters{kConsensusParametersName};

inline constexpr db::MapConfig kChainDataTables[]{
kAccountChangeSet,
kAccountHistory,
Expand Down Expand Up @@ -422,6 +425,7 @@ inline constexpr db::MapConfig kChainDataTables[]{
kTrieOfStorage,
kTxLookup,
kRuntimeStates,
kConsensusParameters,
};

//! \brief Ensures all defined tables are present in db with consistent flags. Should a table not exist it gets created
Expand Down
1 change: 1 addition & 0 deletions silkworm/node/db/util_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ TEST_CASE("BlockBodyForStorage encoding") {
.extra_data = *from_hex("0001FF0100"),
.prev_randao = 0x0000000000000000000000000000000000000000000000000000000000000001_bytes32,
.nonce = {0, 0, 0, 0, 0, 0, 0, 255},
.consensus_parameter_index = 0x1234,
elmato marked this conversation as resolved.
Show resolved Hide resolved
.base_fee_per_gas = 0x244428,
};

Expand Down
5 changes: 5 additions & 0 deletions silkworm/silkrpc/json/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ void to_json(nlohmann::json& json, const BlockHeader& header) {
json["gasLimit"] = rpc::to_quantity(header.gas_limit);
json["gasUsed"] = rpc::to_quantity(header.gas_used);
json["timestamp"] = rpc::to_quantity(header.timestamp);
if (header.consensus_parameter_index.has_value()) {
elmato marked this conversation as resolved.
Show resolved Hide resolved
json["consensusParameterIndex"] = rpc::to_quantity(header.consensus_parameter_index.value_or(0));
} else {
json["consensusParameterIndex"] = nullptr;
}
if (header.base_fee_per_gas.has_value()) {
json["baseFeePerGas"] = rpc::to_quantity(header.base_fee_per_gas.value_or(0));
} else {
Expand Down
Loading
Loading