Skip to content

Commit

Permalink
rpcdaemon: Execution API RPC unit tests (#1352)
Browse files Browse the repository at this point in the history
  • Loading branch information
JacekGlen authored Jul 26, 2023
1 parent 1453f8d commit 3b51f48
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 32 deletions.
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
[submodule "third_party/stun-msg"]
path = third_party/stun-msg
url = https://github.com/battlmonstr/stun-msg.git
[submodule "third_party/execution-apis"]
path = third_party/execution-apis
url = https://github.com/ethereum/execution-apis.git
branch = main
[submodule "third_party/picohttpparser"]
path = third_party/picohttpparser
url = https://github.com/h2o/picohttpparser
2 changes: 1 addition & 1 deletion cmake/run_unit_tests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,5 @@ foreach(TEST_COMMAND IN LISTS TEST_COMMANDS)
set(ENV{LLVM_PROFILE_FILE} "${TEST_COMMAND_NAME}.profraw")
endif()

execute_process(COMMAND "${TEST_COMMAND}" COMMAND_ERROR_IS_FATAL ANY)
execute_process(COMMAND "${TEST_COMMAND}" "~[ignore]" COMMAND_ERROR_IS_FATAL ANY)
endforeach()
52 changes: 51 additions & 1 deletion silkworm/node/db/access_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <silkworm/infra/common/ensure.hpp>
#include <silkworm/node/db/bitmap.hpp>
#include <silkworm/node/db/tables.hpp>
#include <silkworm/node/types/receipt_cbor.hpp>

namespace silkworm::db {

Expand Down Expand Up @@ -64,7 +65,7 @@ void write_schema_version(RWTxn& txn, const VersionBase& schema_version) {
src.upsert(mdbx::slice{kDbSchemaVersionKey}, to_slice(value));
}

void write_build_info_height(RWTxn& txn, Bytes key, BlockNum height) {
void write_build_info_height(RWTxn& txn, const Bytes& key, BlockNum height) {
auto cursor = txn.rw_cursor(db::table::kDatabaseInfo);
Bytes value{db::block_key(height)};
cursor->upsert(db::to_slice(key), db::to_slice(value));
Expand Down Expand Up @@ -498,6 +499,7 @@ void write_body(RWTxn& txn, const BlockBody& body, const evmc::bytes32& hash, Bl
void write_body(RWTxn& txn, const BlockBody& body, const uint8_t (&hash)[kHashLength], const BlockNum number) {
detail::BlockBodyForStorage body_for_storage{};
body_for_storage.ommers = body.ommers;
body_for_storage.withdrawals = body.withdrawals;
body_for_storage.txn_count = body.transactions.size() + 2;
body_for_storage.base_txn_id =
increment_map_sequence(txn, table::kBlockTransactions.name, body_for_storage.txn_count);
Expand All @@ -510,6 +512,22 @@ void write_body(RWTxn& txn, const BlockBody& body, const uint8_t (&hash)[kHashLe
write_transactions(txn, body.transactions, body_for_storage.base_txn_id + 1);
}

void write_raw_body(RWTxn& txn, const BlockBody& body, const evmc::bytes32& hash, BlockNum bn) {
detail::BlockBodyForStorage body_for_storage{};
body_for_storage.ommers = body.ommers;
body_for_storage.withdrawals = body.withdrawals;
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);
Bytes value{body_for_storage.encode()};
auto key{db::block_key(bn, hash.bytes)};

auto target = txn.rw_cursor(table::kBlockBodies);
target->upsert(to_slice(key), to_slice(value));

write_transactions(txn, body.transactions, body_for_storage.base_txn_id);
}

static ByteView read_senders_raw(ROTxn& txn, const Bytes& key) {
auto cursor = txn.ro_cursor(table::kSenders);
auto data{cursor->find(to_slice(key), /*throw_notfound = */ false)};
Expand Down Expand Up @@ -553,6 +571,38 @@ void parse_senders(ROTxn& txn, const Bytes& key, std::vector<Transaction>& out)
}
}

void write_senders(RWTxn& txn, const evmc::bytes32& hash, const BlockNum& block_number, const Block& block) {
auto key{db::block_key(block_number, hash.bytes)};
auto target = txn.rw_cursor(table::kSenders);
Bytes data;
for (const auto& block_txn : block.transactions) {
if (block_txn.from.has_value()) {
data.append(block_txn.from.value().bytes, kAddressLength);
} else {
throw std::runtime_error("Missing senders for block " + std::to_string(block_number));
}
}

target->upsert(to_slice(key), to_slice(data));
}

void write_tx_lookup(RWTxn& txn, const BlockNum& block_number, const Block& block) {
auto target = txn.rw_cursor(table::kTxLookup);
Bytes data;
for (const auto& block_txn : block.transactions) {
auto tx_key = block_txn.hash();
auto tx_data = db::block_key(block_number);
target->upsert(to_slice(tx_key.bytes), to_slice(tx_data));
}
}

void write_receipts(RWTxn& txn, const std::vector<silkworm::Receipt>& receipts, const BlockNum& block_number) {
auto target = txn.rw_cursor(table::kBlockReceipts);
auto key{db::block_key(block_number)};
Bytes value{cbor_encode(receipts)};
target->upsert(to_slice(key), to_slice(value));
}

std::optional<ByteView> read_code(ROTxn& txn, const evmc::bytes32& code_hash) {
auto cursor = txn.ro_cursor(table::kCode);
auto key{to_slice(code_hash)};
Expand Down
8 changes: 7 additions & 1 deletion silkworm/node/db/access_layer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <silkworm/core/types/account.hpp>
#include <silkworm/core/types/block.hpp>
#include <silkworm/core/types/hash.hpp>
#include <silkworm/core/types/receipt.hpp>
#include <silkworm/node/db/mdbx.hpp>
#include <silkworm/node/db/util.hpp>
#include <silkworm/node/snapshot/repository.hpp>
Expand All @@ -44,7 +45,7 @@ void write_schema_version(RWTxn& txn, const VersionBase& schema_version);
//! \brief Updates database info with build info at provided height
//! \details Is useful to track whether increasing heights have been affected by
//! upgrades or downgrades of Silkworm's build
void write_build_info_height(RWTxn& txn, Bytes key, BlockNum height);
void write_build_info_height(RWTxn& txn, const Bytes& key, BlockNum height);

//! \brief Read the list of snapshot file names
std::vector<std::string> read_snapshots(ROTxn& txn);
Expand Down Expand Up @@ -118,6 +119,7 @@ size_t process_blocks_at_height(ROTxn& txn, BlockNum height, std::function<void(
//! \brief Writes block body in table::kBlockBodies
void write_body(RWTxn& txn, const BlockBody& body, const evmc::bytes32& hash, BlockNum bn);
void write_body(RWTxn& txn, const BlockBody& body, const uint8_t (&hash)[kHashLength], BlockNum number);
void write_raw_body(RWTxn& txn, const BlockBody& body, const evmc::bytes32& hash, BlockNum bn);

// See Erigon ReadTd
std::optional<intx::uint256> read_total_difficulty(ROTxn& txn, BlockNum, const evmc::bytes32& hash);
Expand All @@ -144,6 +146,10 @@ std::vector<evmc::address> read_senders(ROTxn& txn, const Bytes& key);
std::vector<evmc::address> read_senders(ROTxn& txn, BlockNum block_number, const uint8_t (&hash)[kHashLength]);
//! \brief Fills transactions' senders addresses directly in place
void parse_senders(ROTxn& txn, const Bytes& key, std::vector<Transaction>& out);
void write_senders(RWTxn& txn, const evmc::bytes32& hash, const BlockNum& number, const Block& block);

void write_tx_lookup(RWTxn& txn, const BlockNum& block_number, const Block& block);
void write_receipts(RWTxn& txn, const std::vector<silkworm::Receipt>& receipts, const BlockNum& block_number);

// See Erigon ReadTransactions
void read_transactions(ROTxn& txn, uint64_t base_id, uint64_t count, std::vector<Transaction>& out);
Expand Down
43 changes: 24 additions & 19 deletions silkworm/silkrpc/commands/eth_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,10 @@ awaitable<void> EthereumRpcApi::handle_eth_chain_id(const nlohmann::json& reques

try {
ethdb::TransactionDatabase tx_database{*tx};
const auto chain_id = co_await core::rawdb::read_chain_id(tx_database);
reply = make_json_content(request["id"], to_quantity(chain_id));
const auto chain_storage{tx->create_storage(tx_database, backend_)};
auto chain_config = co_await chain_storage->read_chain_config();
ensure(chain_config.has_value(), "cannot read chain config");
reply = make_json_content(request["id"], to_quantity((*chain_config).chain_id));
} catch (const std::exception& e) {
SILK_ERROR << "exception: " << e.what() << " processing request: " << request.dump();
reply = make_json_error(request["id"], 100, e.what());
Expand Down Expand Up @@ -911,10 +913,13 @@ awaitable<void> EthereumRpcApi::handle_eth_estimate_gas(const nlohmann::json& re
const BlockNumberOrHash block_number_or_hash{core::kLatestBlockId};
ethdb::kv::CachedDatabase cached_database{block_number_or_hash, *tx, *state_cache_};
ethdb::TransactionDatabase tx_database{*tx};
const auto chain_id = co_await core::rawdb::read_chain_id(tx_database);
const auto chain_config_ptr = lookup_chain_config(chain_id);

const auto chain_storage{tx->create_storage(tx_database, backend_)};
auto chain_config = co_await chain_storage->read_chain_config();
ensure(chain_config.has_value(), "cannot read chain config");

const auto latest_block_number = co_await core::get_block_number(core::kLatestBlockId, tx_database);
SILK_DEBUG << "chain_id: " << chain_id << ", latest_block_number: " << latest_block_number;
SILK_DEBUG << "chain_id: " << (*chain_config).chain_id << ", latest_block_number: " << latest_block_number;

const auto latest_block_with_hash = co_await core::read_block_by_number(*block_cache_, tx_database, latest_block_number);
const auto latest_block = latest_block_with_hash->block;
Expand All @@ -927,7 +932,7 @@ awaitable<void> EthereumRpcApi::handle_eth_estimate_gas(const nlohmann::json& re
return state_reader.read_account(address, block_number + 1);
};

rpc::EstimateGasOracle estimate_gas_oracle{block_header_provider, account_reader, *chain_config_ptr, workers_, *tx, tx_database};
rpc::EstimateGasOracle estimate_gas_oracle{block_header_provider, account_reader, *chain_config, workers_, *tx, tx_database};

auto estimated_gas = co_await estimate_gas_oracle.estimate_gas(call, latest_block);

Expand Down Expand Up @@ -1139,9 +1144,8 @@ awaitable<void> EthereumRpcApi::handle_eth_call(const nlohmann::json& request, s
ethdb::kv::CachedDatabase cached_database{BlockNumberOrHash{block_id}, *tx, *state_cache_};

const auto chain_storage{tx->create_storage(tx_database, backend_)};
const auto chain_id = co_await chain_storage->read_chain_id();
ensure(chain_id.has_value(), "cannot read chain ID");
const auto chain_config = lookup_chain_config(*chain_id);
auto chain_config = co_await chain_storage->read_chain_config();
ensure(chain_config.has_value(), "cannot read chain config");
const auto [block_number, is_latest_block] = co_await core::get_block_number(block_id, tx_database, /*latest_required=*/true);

const auto block_with_hash = co_await core::read_block_by_number(*block_cache_, *chain_storage, tx_database, block_number);
Expand Down Expand Up @@ -1294,9 +1298,8 @@ awaitable<void> EthereumRpcApi::handle_eth_create_access_list(const nlohmann::js
const auto chain_storage{tx->create_storage(tx_database, backend_)};
const auto block_with_hash = co_await core::read_block_by_number_or_hash(*block_cache_, *chain_storage, tx_database, block_number_or_hash);

const auto chain_id = co_await chain_storage->read_chain_id();
ensure(chain_id.has_value(), "cannot read chain ID");
const auto chain_config_ptr = lookup_chain_config(*chain_id);
auto chain_config = co_await chain_storage->read_chain_config();
ensure(chain_config.has_value(), "cannot read chain config");

const bool is_latest_block = co_await core::get_latest_executed_block_number(tx_database) == block_with_hash->block.header.number;
const core::rawdb::DatabaseReader& db_reader =
Expand Down Expand Up @@ -1335,7 +1338,7 @@ awaitable<void> EthereumRpcApi::handle_eth_create_access_list(const nlohmann::js
tracer->reset_access_list();

const auto execution_result = co_await EVMExecutor::call(
*chain_config_ptr, workers_, block_with_hash->block, txn, [&](auto& io_executor, auto block_num) {
*chain_config, workers_, block_with_hash->block, txn, [&](auto& io_executor, auto block_num) {
return tx->create_state(io_executor, db_reader, block_num);
},
tracers, /* refund */ true, /* gasBailout */ false);
Expand Down Expand Up @@ -1402,8 +1405,9 @@ awaitable<void> EthereumRpcApi::handle_eth_call_bundle(const nlohmann::json& req
ethdb::kv::CachedDatabase cached_database{block_number_or_hash, *tx, *state_cache_};

const auto block_with_hash = co_await core::read_block_by_number_or_hash(*block_cache_, tx_database, block_number_or_hash);
const auto chain_id = co_await core::rawdb::read_chain_id(tx_database);
const auto chain_config_ptr = lookup_chain_config(chain_id);
const auto chain_storage{tx->create_storage(tx_database, backend_)};
auto chain_config = co_await chain_storage->read_chain_config();
ensure(chain_config.has_value(), "cannot read chain config");

const bool is_latest_block = co_await core::get_latest_executed_block_number(tx_database) == block_with_hash->block.header.number;
const core::rawdb::DatabaseReader& db_reader =
Expand All @@ -1427,7 +1431,7 @@ awaitable<void> EthereumRpcApi::handle_eth_call_bundle(const nlohmann::json& req
}

const auto execution_result = co_await EVMExecutor::call(
*chain_config_ptr, workers_, block_with_hash->block, tx_with_block->transaction, [&](auto& io_executor, auto block_num) {
*chain_config, workers_, block_with_hash->block, tx_with_block->transaction, [&](auto& io_executor, auto block_num) {
return tx->create_state(io_executor, db_reader, block_num);
});
if (execution_result.pre_check_error) {
Expand Down Expand Up @@ -2097,10 +2101,11 @@ awaitable<void> EthereumRpcApi::handle_fee_history(const nlohmann::json& request
return core::get_receipts(tx_database, block_with_hash);
};

const auto chain_id = co_await core::rawdb::read_chain_id(tx_database);
const auto chain_config_ptr = lookup_chain_config(chain_id);
const auto chain_storage{tx->create_storage(tx_database, backend_)};
auto chain_config = co_await chain_storage->read_chain_config();
ensure(chain_config.has_value(), "cannot read chain config");

rpc::fee_history::FeeHistoryOracle oracle{*chain_config_ptr, block_provider, receipts_provider};
rpc::fee_history::FeeHistoryOracle oracle{*chain_config, block_provider, receipts_provider};

const auto block_number = co_await core::get_block_number(newest_block, tx_database);
auto fee_history = co_await oracle.fee_history(block_number, block_count, reward_percentile);
Expand Down
Loading

0 comments on commit 3b51f48

Please sign in to comment.