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 @@ -94,6 +94,7 @@ endif()

# Silkworm itself
add_subdirectory(silkworm)
add_subdirectory(eosevm)

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

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
evmc
tl::expected
nlohmann_json::nlohmann_json
)

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

#if not defined(ANTELOPE)
#include <silkworm/core/common/assert.hpp>
#include <silkworm/core/common/endian.hpp>
#endif

namespace eosevm {
bool operator==(const eosevm::GasFeeParameters& a, const eosevm::GasFeeParameters& b) {
return a.gas_codedeposit == b.gas_codedeposit && a.gas_newaccount == b.gas_newaccount &&
a.gas_sset == b.gas_sset && a.gas_txcreate == b.gas_txcreate && a.gas_txnewaccount == b.gas_txnewaccount;
}

bool operator==(const eosevm::ConsensusParameters& a, const eosevm::ConsensusParameters& b) {
return a.min_gas_price == b.min_gas_price && a.gas_fee_parameters == b.gas_fee_parameters; }


#if not defined(ANTELOPE)
[[nodiscard]] silkworm::Bytes GasFeeParameters::encode() const noexcept {
silkworm::Bytes ret(40, '\0');
silkworm::endian::store_big_u64(&ret[0], gas_txnewaccount);
silkworm::endian::store_big_u64(&ret[8], gas_newaccount);
silkworm::endian::store_big_u64(&ret[16], gas_txcreate);
silkworm::endian::store_big_u64(&ret[24], gas_codedeposit);
silkworm::endian::store_big_u64(&ret[32], gas_sset);

return ret;
}

std::optional<GasFeeParameters> GasFeeParameters::decode(silkworm::ByteView encoded) noexcept {
SILKWORM_ASSERT(encoded.length() >= 40);
GasFeeParameters feeParams;
feeParams.gas_txnewaccount = silkworm::endian::load_big_u64(&encoded[0]);
feeParams.gas_newaccount = silkworm::endian::load_big_u64(&encoded[8]);
feeParams.gas_txcreate = silkworm::endian::load_big_u64(&encoded[16]);
feeParams.gas_codedeposit = silkworm::endian::load_big_u64(&encoded[24]);
feeParams.gas_sset = silkworm::endian::load_big_u64(&encoded[32]);

return feeParams;
}
#endif

#if not defined(ANTELOPE)
[[nodiscard]] silkworm::Bytes ConsensusParameters::encode() const noexcept {
SILKWORM_ASSERT(min_gas_price.has_value());
SILKWORM_ASSERT(gas_fee_parameters.has_value());
constexpr size_t size_before_fee_param = 2 * sizeof(uint64_t);
auto value = gas_fee_parameters->encode();
silkworm::Bytes ret(value.length() + size_before_fee_param, '\0');
// Always store as latest supported version: currently 0.
silkworm::endian::store_big_u64(&ret[0], 0);
silkworm::endian::store_big_u64(&ret[sizeof(uint64_t)], *min_gas_price);
std::memcpy(&ret[size_before_fee_param], &value[0], value.length());
return ret;
};

std::optional<ConsensusParameters> ConsensusParameters::decode(silkworm::ByteView encoded) noexcept {
SILKWORM_ASSERT(encoded.length() > sizeof(uint64_t));
ConsensusParameters config{};
const auto version = silkworm::endian::load_big_u64(&encoded[0]);

// Parse according to version. For now, only 0.
switch (version) {
case 0: {
constexpr size_t size_before_fee_param = 2 * sizeof(uint64_t);
SILKWORM_ASSERT(encoded.length() > size_before_fee_param);
config.min_gas_price = silkworm::endian::load_big_u64(&encoded[sizeof(uint64_t)]);
config.gas_fee_parameters = GasFeeParameters::decode(silkworm::ByteView{&encoded[size_before_fee_param], encoded.length() - size_before_fee_param});
break;
}
default: SILKWORM_ASSERT(version <= 0);
}

return config;
}
#endif

} // namespace eosevm
57 changes: 57 additions & 0 deletions eosevm/consensus_parameters.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#pragma once

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

#include <intx/intx.hpp>


#if not defined(ANTELOPE)
#include <silkworm/core/common/base.hpp>
#endif


namespace eosevm {

// Note: GasFeeParameters struct is NOT versioned, version will be handled by ConsensusParameters.
// If we want to change this struct, create GasFeeParametersV2 and let ConsensusParameters use it.
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)
// Encode for storage in db.
[[nodiscard]] silkworm::Bytes encode() const noexcept;

// Decode from storage in db.
static std::optional<GasFeeParameters> decode(silkworm::ByteView encoded) noexcept;
#endif

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

struct ConsensusParameters {
std::optional<uint64_t> min_gas_price;
std::optional<GasFeeParameters> gas_fee_parameters;

#if not defined(ANTELOPE)
// Encode for storage in db.
[[nodiscard]] silkworm::Bytes encode() const noexcept;

// Decode from storage in db.
static std::optional<ConsensusParameters> decode(silkworm::ByteView encoded) noexcept;
#endif

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

} // namespace eosevm
29 changes: 28 additions & 1 deletion silkworm/core/types/block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ bool operator==(const BlockHeader& a, const BlockHeader& b) {
}

bool operator==(const BlockBody& a, const BlockBody& b) {
return a.transactions == b.transactions && a.ommers == b.ommers;
return a.transactions == b.transactions && a.ommers == b.ommers && a.consensus_parameter_index == b.consensus_parameter_index;
}

BlockNum height(const BlockId& b) { return b.number; }
Expand Down Expand Up @@ -241,6 +241,9 @@ namespace rlp {
Header rlp_head{.list = true};
rlp_head.payload_length += length(b.transactions);
rlp_head.payload_length += length(b.ommers);
if (b.consensus_parameter_index) {
rlp_head.payload_length += length(*b.consensus_parameter_index);
}
if (b.withdrawals) {
rlp_head.payload_length += length(*b.withdrawals);
}
Expand All @@ -256,6 +259,9 @@ namespace rlp {
encode_header(to, rlp_header_body(block_body));
encode(to, block_body.transactions);
encode(to, block_body.ommers);
if (block_body.consensus_parameter_index) {
encode(to, *block_body.consensus_parameter_index);
}
if (block_body.withdrawals) {
encode(to, *block_body.withdrawals);
}
Expand All @@ -278,6 +284,15 @@ namespace rlp {
return res;
}

to.consensus_parameter_index = std::nullopt;
if (from.length() > leftover) {
uint64_t consensus_parameter_index;
if (DecodingResult res{decode(from, consensus_parameter_index, Leftover::kAllow)}; !res) {
return res;
}
to.consensus_parameter_index = consensus_parameter_index;
}

to.withdrawals = std::nullopt;
if (from.length() > leftover) {
std::vector<Withdrawal> withdrawals;
Expand Down Expand Up @@ -310,6 +325,15 @@ namespace rlp {
return res;
}

to.consensus_parameter_index = std::nullopt;
if (from.length() > leftover) {
uint64_t consensus_parameter_index;
if (DecodingResult res{decode(from, consensus_parameter_index, Leftover::kAllow)}; !res) {
return res;
}
to.consensus_parameter_index = consensus_parameter_index;
}

to.withdrawals = std::nullopt;
if (from.length() > leftover) {
std::vector<Withdrawal> withdrawals;
Expand Down Expand Up @@ -341,6 +365,9 @@ namespace rlp {
encode(to, block.header);
encode(to, block.transactions);
encode(to, block.ommers);
if (block.consensus_parameter_index) {
encode(to, *block.consensus_parameter_index);
}
if (block.withdrawals) {
encode(to, *block.withdrawals);
}
Expand Down
6 changes: 6 additions & 0 deletions silkworm/core/types/block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include <silkworm/core/types/transaction.hpp>
#include <silkworm/core/types/withdrawal.hpp>

#include <eosevm/consensus_parameters.hpp>

namespace silkworm {

using TotalDifficulty = intx::uint256;
Expand Down Expand Up @@ -94,13 +96,17 @@ struct BlockBody {
std::vector<BlockHeader> ommers;
std::optional<std::vector<Withdrawal>> withdrawals{std::nullopt};

// EOS-EVM
std::optional<uint64_t> consensus_parameter_index{std::nullopt};

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

struct Block : public BlockBody {
BlockHeader header;

bool irreversible{false};
std::optional<eosevm::ConsensusParameters> consensus_parameters_cache;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to have this cache here?
I thought it will be local to the function that prepares the execution of the block

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is because we are currently only passing block around: from block_conversion_plugin to blockchain_plugin, from blockchain_plugin to the executor

It will be safer to copy and pass this piece of information with block. otherwise we will have to let executor read the new parameters from somewhere, and that will start to encounter thread safety issues.

This approach might not be ideal, Feel free to suggest other approaches.

void recover_senders();
};

Expand Down
2 changes: 2 additions & 0 deletions silkworm/core/types/block_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ TEST_CASE("BlockBody RLP 2") {
body.ommers[0].prev_randao = 0xf0a53dfdd6c2f2a661e718ef29092de60d81d45f84044bec7bf4b36630b2bc08_bytes32;
body.ommers[0].nonce[7] = 35;

body.consensus_parameter_index = 1234;

Bytes rlp{};
rlp::encode(rlp, body);

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
20 changes: 20 additions & 0 deletions silkworm/node/db/access_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ bool read_body(ROTxn& txn, const Bytes& key, bool read_senders, BlockBody& out)
if (!out.transactions.empty() && read_senders) {
parse_senders(txn, key, out.transactions);
}
out.consensus_parameter_index = body.consensus_parameter_index;
return true;
}

Expand Down Expand Up @@ -498,6 +499,7 @@ void write_body(RWTxn& txn, const BlockBody& body, const uint8_t (&hash)[kHashLe
body_for_storage.txn_count = body.transactions.size();
body_for_storage.base_txn_id =
increment_map_sequence(txn, table::kBlockTransactions.name, body_for_storage.txn_count);
body_for_storage.consensus_parameter_index = body.consensus_parameter_index;
Bytes value{body_for_storage.encode()};
auto key{db::block_key(number, hash)};

Expand Down Expand Up @@ -1217,4 +1219,22 @@ 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;
}
const auto encoded = from_slice(data.value);
return eosevm::ConsensusParameters::decode(encoded);
}

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)};

cursor->upsert(to_slice(key), mdbx::slice(config.encode()));
}

} // 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
Loading
Loading