Skip to content

Update to peer_keys_db_t to match changes in eos-system-contracts pr 185 #1316

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

Merged
merged 35 commits into from
Apr 16, 2025
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
bc78038
Update `peer_keys_db` to use the ro transaction result.
greg7mdp Mar 28, 2025
696f02b
Implement `get_top_producer_keys` and factorize dup. code.
greg7mdp Mar 31, 2025
dba098c
Add code to test the ro action `getpeerkeys`
greg7mdp Mar 31, 2025
63731f2
Make the `getpeerkeys` return type nicer.
greg7mdp Mar 31, 2025
b1e64cc
Checkin state before trying to avoid table changes in `~system_contra…
greg7mdp Apr 1, 2025
00b9582
To avoid the non-readonly destructor, implement `getpeerkeys` in sepa…
greg7mdp Apr 1, 2025
ac600cf
Update `getpeerkeys` result type to match system contracts.
greg7mdp Apr 3, 2025
4b9364a
Update peer keys on `accepted_block`.
greg7mdp Apr 3, 2025
328d2fe
Undo whitespace change
greg7mdp Apr 3, 2025
1bd34ed
implement `get_peer_info`
greg7mdp Apr 3, 2025
70edeab
Need to find the right location in the code to call `update_peer_keys`
greg7mdp Apr 3, 2025
b07e4d5
Merge branch 'main' of github.com:AntelopeIO/spring into gh_1183_3
greg7mdp Apr 4, 2025
27075d6
Update contracts and tests to match `eos-system-contracts`
greg7mdp Apr 4, 2025
b58e9c1
Remove outdated comment.
greg7mdp Apr 4, 2025
bce86eb
Merge branch 'main' of github.com:AntelopeIO/spring into gh_1183_3
greg7mdp Apr 4, 2025
77c2123
Push wasm file compiled with cdt @ commit #b949569188f9d8bfaec8d40fa0…
greg7mdp Apr 7, 2025
7e70919
Move `update_peer_keys` when we have a `building_block.
greg7mdp Apr 8, 2025
8f36e60
Merge branch 'main' of github.com:AntelopeIO/spring into gh_1183_3
greg7mdp Apr 8, 2025
bdbe42f
Call ro action from producer_plugin.
greg7mdp Apr 8, 2025
d440317
test
greg7mdp Apr 8, 2025
b3f3693
Be tolerant of the `getpeerkeys` action not being available in some i…
greg7mdp Apr 8, 2025
9c783fd
Update `push_transaction` params, ignore exceptions.
greg7mdp Apr 8, 2025
f694d61
Update a couple comments.
greg7mdp Apr 11, 2025
68bc98b
Merge branch 'main' into gh_1183_3
greg7mdp Apr 11, 2025
6342b5d
Update comments referring to top-50 producers.
greg7mdp Apr 14, 2025
406d928
Add comment in `peer_keys` test contract.
greg7mdp Apr 14, 2025
e80a6e5
Remove outdated comment.
greg7mdp Apr 14, 2025
ca9c2f1
Allow a max of 20ms for getpeerkeys
greg7mdp Apr 14, 2025
53770de
Merge branch 'main' of github.com:AntelopeIO/spring into gh_1183_3
greg7mdp Apr 14, 2025
a42f0b8
Merge branch 'gh_1183_3' of github.com:AntelopeIO/spring into gh_1183_3
greg7mdp Apr 14, 2025
9ca911a
Give `run_readonly_transactions` a more descriptive name.
greg7mdp Apr 14, 2025
59e9336
rename function to `update_peer_keys`.
greg7mdp Apr 14, 2025
de28545
Remove producers not in top-50 anymore from `_peer_info_map`.
greg7mdp Apr 14, 2025
f2e0543
Address PR comments.
greg7mdp Apr 16, 2025
88a9855
Merge branch 'main' of github.com:AntelopeIO/spring into gh_1183_3
greg7mdp Apr 16, 2025
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
95 changes: 74 additions & 21 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,59 @@ struct controller_impl {
apply_handlers[receiver][make_pair(contract,action)] = v;
}

void set_trx_expiration(signed_transaction& trx) {
if (is_builtin_activated(builtin_protocol_feature_t::no_duplicate_deferred_id)) {
trx.expiration = time_point_sec();
trx.ref_block_num = 0;
trx.ref_block_prefix = 0;
} else {
trx.expiration = time_point_sec{
pending_block_time() + fc::microseconds(999'999)}; // Round up to nearest second to avoid appearing expired
trx.set_reference_block(chain_head.id());
}
}

getpeerkeys_res_t get_top_producer_keys() {
try {
auto get_getpeerkeys_transaction = [&]() {
auto perms = vector<permission_level>{};
action act(perms, config::system_account_name, "getpeerkeys"_n, {});
signed_transaction trx;

trx.actions.emplace_back(std::move(act));
trx.set_reference_block(chain_head.id());
set_trx_expiration(trx);
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we require tapos & expiration on read only trx? You might be able to skip these couple lines

Either way, set_trx_expiration() does a set_reference_block() anyways so seems at least that line is redundant

return trx;
};

auto metadata = transaction_metadata::create_no_recover_keys(
std::make_shared<packed_transaction>(get_getpeerkeys_transaction()),
transaction_metadata::trx_type::read_only);

const auto& gpo = db.get<global_property_object>();

auto trace = push_transaction(metadata, fc::time_point::maximum(), fc::microseconds::maximum(),
gpo.configuration.min_transaction_cpu_usage, true, 0);
Copy link
Member

Choose a reason for hiding this comment

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

No need to use gpo.configuration.min_transaction_cpu_usage for read-only, use 0 for billed_cpu_time_us. explicit_billed_cpu_time should be false not true. I think max_transaction_time can probably be left as maximum although we might consider using a smaller value. For block_deadline, if you are going to call this from producer_plugin then I think you should go ahead and use the actual block_deadline.

Copy link
Member

Choose a reason for hiding this comment

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

@arhag any opinion on if we should set a limit on the read-only trx? Maybe something like 10ms?

Copy link
Contributor Author

Choose a reason for hiding this comment

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


if( trace->except_ptr )
std::rethrow_exception(trace->except_ptr);
if( trace->except)
throw *trace->except;
getpeerkeys_res_t res;
if (!trace->action_traces.empty()) {
const auto& act_trace = trace->action_traces[0];
const auto& retval = act_trace.return_value;
assert(!retval.empty());

fc::datastream<const char*> ds(retval.data(), retval.size());
fc::raw::unpack(ds, res);
}

return res;
}
FC_LOG_AND_RETHROW()
}

controller_impl( const controller::config& cfg, controller& s, protocol_feature_set&& pfs, const chain_id_type& chain_id )
:rnh(),
self(s),
Expand Down Expand Up @@ -1322,9 +1375,6 @@ struct controller_impl {
const auto& [ block, id] = t;
wasmif.current_lib(block->block_num());
vote_processor.notify_lib(block->block_num());

// update peer public keys from chainbase db
peer_keys_db.update_peer_keys(self, block->block_num());
});

#define SET_APP_HANDLER( receiver, contract, action) \
Expand Down Expand Up @@ -2623,14 +2673,7 @@ struct controller_impl {
// Deliver onerror action containing the failed deferred transaction directly back to the sender.
etrx.actions.emplace_back( vector<permission_level>{{gtrx.sender, config::active_name}},
onerror( gtrx.sender_id, gtrx.packed_trx.data(), gtrx.packed_trx.size() ) );
if( is_builtin_activated( builtin_protocol_feature_t::no_duplicate_deferred_id ) ) {
etrx.expiration = time_point_sec();
etrx.ref_block_num = 0;
etrx.ref_block_prefix = 0;
} else {
etrx.expiration = time_point_sec{pending_block_time() + fc::microseconds(999'999)}; // Round up to nearest second to avoid appearing expired
etrx.set_reference_block( chain_head.id() );
}
set_trx_expiration(etrx);

auto& bb = std::get<building_block>(pending->_block_stage);

Expand Down Expand Up @@ -3339,9 +3382,18 @@ struct controller_impl {
}

guard_pending.cancel();

return onblock_trace;
} /// start_block

void run_readonly_transactions() {
// update peer public keys from chainbase db using a readonly trx
auto block_num = chain_head.block_num();
if (block_num % 120 == 0) {
peer_keys_db.update_peer_keys(get_top_producer_keys()); // update once/minute
}
}

void assemble_block(bool validating, std::optional<qc_data_t> validating_qc_data, const block_state_ptr& validating_bsp)
{
EOS_ASSERT( pending, block_validate_exception, "it is not valid to finalize when there is no pending block");
Expand Down Expand Up @@ -4788,14 +4840,7 @@ struct controller_impl {

signed_transaction trx;
trx.actions.emplace_back(std::move(on_block_act));
if( is_builtin_activated( builtin_protocol_feature_t::no_duplicate_deferred_id ) ) {
trx.expiration = time_point_sec();
trx.ref_block_num = 0;
trx.ref_block_prefix = 0;
} else {
trx.expiration = time_point_sec{pending_block_time() + fc::microseconds(999'999)}; // Round up to nearest second to avoid appearing expired
trx.set_reference_block( chain_head.id() );
}
set_trx_expiration(trx);

return trx;
}
Expand Down Expand Up @@ -5293,6 +5338,10 @@ transaction_trace_ptr controller::start_block( block_timestamp_type when,
bs, std::optional<block_id_type>(), deadline );
}

void controller::run_readonly_transactions() {
my->run_readonly_transactions();
}

void controller::assemble_and_complete_block( const signer_callback_type& signer_callback ) {
validate_db_available_size();

Expand Down Expand Up @@ -5793,8 +5842,12 @@ void controller::set_peer_keys_retrieval_active(bool active) {
my->peer_keys_db.set_active(active);
}

std::optional<public_key_type> controller::get_peer_key(name n) const {
return my->peer_keys_db.get_peer_key(n);
peer_info_t controller::get_peer_info(name n) const {
return my->peer_keys_db.get_peer_info(n);
}

getpeerkeys_res_t controller::get_top_producer_keys() {
return my->get_top_producer_keys();
}

db_read_mode controller::get_read_mode()const {
Expand Down
22 changes: 21 additions & 1 deletion libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,22 @@ namespace eosio::chain {
class resource_limits_manager;
};

// vector, sorted by rank, of the paid producer names and their peer key
// if populated on-chain.
// -----------------------------------------------------------------------
struct peerkeys_t {
name producer_name;
std::optional<public_key_type> peer_key;
};
using getpeerkeys_res_t = std::vector<peerkeys_t>;

struct peer_info_t {
uint32_t rank;
std::optional<public_key_type> key;

bool operator==(const peer_info_t&) const = default;
};

struct controller_impl;
using chainbase::database;
using chainbase::pinnable_mapped_file;
Expand Down Expand Up @@ -230,6 +246,7 @@ namespace eosio::chain {
void assemble_and_complete_block( const signer_callback_type& signer_callback );
void sign_block( const signer_callback_type& signer_callback );
void commit_block();
void run_readonly_transactions();
void testing_allow_voting(bool val);
bool get_testing_allow_voting_flag();
void set_async_voting(async_t val);
Expand Down Expand Up @@ -428,7 +445,8 @@ namespace eosio::chain {
chain_id_type get_chain_id()const;

void set_peer_keys_retrieval_active(bool active);
std::optional<public_key_type> get_peer_key(name n) const; // thread safe
peer_info_t get_peer_info(name n) const; // thread safe
getpeerkeys_res_t get_top_producer_keys(); // must be called from main thread

// thread safe
db_read_mode get_read_mode()const;
Expand Down Expand Up @@ -515,3 +533,5 @@ namespace eosio::chain {
}; // controller

} /// eosio::chain

FC_REFLECT(eosio::chain::peerkeys_t, (producer_name)(peer_key))
26 changes: 13 additions & 13 deletions libraries/chain/include/eosio/chain/peer_keys_db.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,34 @@ namespace eosio::chain {
*/
class peer_keys_db_t {
public:
struct v0_data { // must match the one in eosio.system.hpp
std::optional<public_key_type> pubkey;
};

using peer_key_map_t = boost::unordered_flat_map<name, public_key_type, std::hash<name>>;
using peer_key_map_t = boost::unordered_flat_map<name, peer_info_t, std::hash<name>>;
using new_peers_t = flat_set<name>;

peer_keys_db_t();

void set_active(bool b) { _active = b; }

// must be called from main thread
size_t update_peer_keys(const controller& chain, uint32_t lib_number);

// safe to be called from any thread
std::optional<public_key_type> get_peer_key(name n) const;
// return the new peers either:
// - added to the top-50
// - removed from the top-50
// - whose key changed
// since the last call to update_peer_keys
// ---------------------------------------
new_peers_t update_peer_keys(const getpeerkeys_res_t& v);

// safe to be called from any thread
size_t size() const;
// peers no longer in top-50 will have a rank of std::numeric_limits<uint32_t>::max()
// ----------------------------------------------------------------------------------
peer_info_t get_peer_info(name n) const;

private:
std::optional<uint64_t> _get_version(const chainbase::database& db);

bool _active = false; // if not active (the default), no update occurs
uint32_t _block_num = 0; // below map includes keys registered up to _block_num (inclusive)
mutable fc::mutex _m;
peer_key_map_t _peer_key_map GUARDED_BY(_m);
peer_key_map_t _peer_info_map GUARDED_BY(_m);
};

} // namespace eosio::chain

FC_REFLECT(eosio::chain::peer_keys_db_t::v0_data, (pubkey))
107 changes: 34 additions & 73 deletions libraries/chain/peer_keys_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,85 +5,46 @@ namespace eosio::chain {

peer_keys_db_t::peer_keys_db_t() : _active(false) {}

std::optional<public_key_type> peer_keys_db_t::get_peer_key(name n) const {
peer_info_t peer_keys_db_t::get_peer_info(name n) const {
fc::lock_guard g(_m);
if (auto it = _peer_key_map.find(n); it != _peer_key_map.end())
return std::optional<public_key_type>(it->second);
return std::optional<public_key_type>{};
assert(_active);
if (auto it = _peer_info_map.find(n); it != _peer_info_map.end())
return it->second;
return peer_info_t{};
}

size_t peer_keys_db_t::size() const {
fc::lock_guard g(_m);
return _peer_key_map.size();
}

// we update the keys that were registered up to lib_number (inclusive)
// --------------------------------------------------------------------
size_t peer_keys_db_t::update_peer_keys(const controller& chain, uint32_t lib_number) {
size_t num_updated = 0;
if (!_active || lib_number <= _block_num)
return num_updated; // nothing to do

try {
const auto& db = chain.db();
const auto table_ref = boost::make_tuple("eosio"_n, "eosio"_n, "peerkeys"_n);
const auto* t_id = db.find<table_id_object, by_code_scope_table>(table_ref);
EOS_ASSERT(t_id != nullptr, misc_exception, "cannot retrieve `peerkeys` table");
peer_keys_db_t::new_peers_t peer_keys_db_t::update_peer_keys(const getpeerkeys_res_t& v) {
if (!_active)
return {};

// create hash_map of current top-60
// ---------------------------------
peer_key_map_t current;
for (size_t i=0; i<v.size(); ++i)
current[v[i].producer_name] = peer_info_t{static_cast<uint32_t>(i), v[i].peer_key};

const auto& secidx = db.get_index<index64_index, by_secondary>();

const auto lower = secidx.lower_bound(std::make_tuple(t_id->id._id, static_cast<uint64_t>(_block_num + 1)));
const auto upper = secidx.upper_bound(std::make_tuple(t_id->id._id, static_cast<uint64_t>(lib_number)));

if (upper == lower) {
// no new keys registered
_block_num = lib_number;
return num_updated;
fc::lock_guard g(_m);
new_peers_t res;

// update ranking of those removed from top-60
// -------------------------------------------
for (auto& pi : _peer_info_map) {
if (!current.contains(pi.first)) {
pi.second.rank = std::numeric_limits<uint32_t>::max();
res.insert(pi.first);
}

fc::lock_guard g(_m); // we only need to protect access to _peer_key_map

for (auto itr = lower; itr != upper; ++itr) {
try {
const auto* itr2 =
db.find<key_value_object, by_scope_primary>(boost::make_tuple(t_id->id, itr->primary_key));

name row_name;
uint32_t row_block_num;
uint8_t row_version;
std::variant<v0_data> row_variant;

const auto& obj = *itr2;
fc::datastream<const char*> ds(obj.value.data(), obj.value.size());
// must match `struct peer_key;` in eosio.system.hpp
// -------------------------------------------------
fc::raw::unpack(ds, row_name);
EOS_ASSERT(row_name.good(), misc_exception, "deserialized invalid name from `peerkeys`");

fc::raw::unpack(ds, row_block_num);
EOS_ASSERT(row_block_num > static_cast<uint64_t>(_block_num), misc_exception,
"deserialized invalid version from `peerkeys`");

fc::raw::unpack(ds, row_version);
if (row_version != 0)
continue;

fc::raw::unpack(ds, row_variant);
EOS_ASSERT(std::holds_alternative<v0_data>(row_variant), misc_exception, "deserialized invalid data from `peerkeys`");
auto& data = std::get<v0_data>(row_variant);
if (data.pubkey) {
EOS_ASSERT(data.pubkey->valid(), misc_exception, "deserialized invalid public key from `peerkeys`");

_peer_key_map[row_name] = *data.pubkey;
++num_updated;
}
}
FC_LOG_AND_DROP(("skipping invalid record deserialized from `peerkeys`"));
}

// add new ones to _peer_info_map and updated modified ones
// --------------------------------------------------------
for (auto& pi : current) {
if (!_peer_info_map.contains(pi.first) || _peer_info_map[pi.first] != pi.second) {
_peer_info_map[pi.first] = pi.second;
res.insert(pi.first);
}

_block_num = lib_number; // mark that we have updated up to lib_number
} FC_LOG_AND_DROP(("Error when updating peer_keys_db"));
return num_updated;
}

return res;
}

} // namespace eosio::chain
2 changes: 2 additions & 0 deletions plugins/producer_plugin/producer_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2347,6 +2347,8 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() {
return start_block_result::exhausted;
}

// chain.run_readonly_transactions();

if (!process_incoming_trxs(preprocess_deadline, incoming_itr))
return start_block_result::exhausted;

Expand Down
2 changes: 1 addition & 1 deletion unittests/contracts/eosio.system/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
if( EOSIO_COMPILE_TEST_CONTRACTS )
add_contract( eosio.system eosio.system eosio.system.cpp )
add_contract( eosio.system eosio.system eosio.system.cpp peer_keys.cpp )
target_include_directories(eosio.system
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
Expand Down
Loading