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

set_finalizers host function (#1511) #1561

Merged
merged 19 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
25 changes: 23 additions & 2 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,10 @@ struct controller_impl {
map< account_name, map<handler_key, apply_handler> > apply_handlers;
unordered_map< builtin_protocol_feature_t, std::function<void(controller_impl&)>, enum_hash<builtin_protocol_feature_t> > protocol_feature_activation_handlers;

// TODO: This probably wants to be something better;
// Storing when set_finalizers() is called; retrievable via get_finalizers() (called by chain_pacemaker)
uint64_t fthreshold;
heifner marked this conversation as resolved.
Show resolved Hide resolved
vector<finalizer_authority> finalizers;

void pop_block() {
auto prev = fork_db.get_block( head->header.previous );
Expand Down Expand Up @@ -1990,6 +1994,16 @@ struct controller_impl {
emit( self.new_hs_new_block_message, msg );
}

void set_finalizers_impl(uint64_t fthreshold, vector<finalizer_authority> finalizers) {
this->fthreshold = fthreshold;
this->finalizers = finalizers;
fcecin marked this conversation as resolved.
Show resolved Hide resolved
}

void get_finalizers_impl(uint64_t& fthreshold, vector<finalizer_authority>& finalizers) {
fthreshold = this->fthreshold;
finalizers = this->finalizers;
}
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved

/**
* This method is called from other threads. The controller_impl should outlive those threads.
* However, to avoid race conditions, it means that the behavior of this function should not change
Expand Down Expand Up @@ -3313,6 +3327,14 @@ int64_t controller::set_proposed_producers( vector<producer_authority> producers
return version;
}

void controller::set_finalizers( uint64_t fthreshold, vector<finalizer_authority> finalizers ) {
my->set_finalizers_impl(fthreshold, std::move(finalizers));
}

void controller::get_finalizers( uint64_t& fthreshold, vector<finalizer_authority>& finalizers ) {
my->get_finalizers_impl(fthreshold, finalizers);
}
fcecin marked this conversation as resolved.
Show resolved Hide resolved

const producer_authority_schedule& controller::active_producers()const {
if( !(my->pending) )
return my->head->active_schedule;
Expand Down Expand Up @@ -3876,8 +3898,7 @@ void controller_impl::on_activation<builtin_protocol_feature_t::bls_primitives>(
template<>
void controller_impl::on_activation<builtin_protocol_feature_t::instant_finality>() {
db.modify( db.get<protocol_state_object>(), [&]( auto& ps ) {
#warning host functions to set proposers, leaders, finalizers/validators
// FIXME/TODO: host functions to set proposers, leaders, finalizers/validators
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "set_finalizers" );
} );
}

Expand Down
4 changes: 4 additions & 0 deletions libraries/chain/include/eosio/chain/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ const static int max_producers = 125;
const static size_t maximum_tracked_dpos_confirmations = 1024; ///<
static_assert(maximum_tracked_dpos_confirmations >= ((max_producers * 2 / 3) + 1) * producer_repetitions, "Settings never allow for DPOS irreversibility" );

/**
* Maximum number of finalizers in the finalizer set
*/
const static int max_finalizers = max_producers;

/**
* The number of blocks produced per round is based upon all producers having a chance
Expand Down
6 changes: 5 additions & 1 deletion libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <eosio/chain/protocol_feature_manager.hpp>
#include <eosio/chain/webassembly/eos-vm-oc/config.hpp>

#include <eosio/chain/finalizer_set.hpp>
heifner marked this conversation as resolved.
Show resolved Hide resolved

namespace chainbase {
class database;
}
Expand Down Expand Up @@ -306,6 +308,9 @@ namespace eosio { namespace chain {

int64_t set_proposed_producers( vector<producer_authority> producers );

void set_finalizers( uint64_t fthreshold, vector<finalizer_authority> finalizers );
void get_finalizers( uint64_t& fthreshold, vector<finalizer_authority>& finalizers );

bool light_validation_allowed() const;
bool skip_auth_check()const;
bool skip_trx_checks()const;
Expand Down Expand Up @@ -395,7 +400,6 @@ namespace eosio { namespace chain {
chainbase::database& mutable_db()const;

std::unique_ptr<controller_impl> my;

};

} } /// eosio::chain
144 changes: 144 additions & 0 deletions libraries/chain/include/eosio/chain/finalizer_set.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#pragma once

#include <eosio/chain/config.hpp>
#include <eosio/chain/types.hpp>
#include <chainbase/chainbase.hpp>
#include <eosio/chain/authority.hpp>
#include <eosio/chain/snapshot.hpp>

#include <fc/crypto/bls_public_key.hpp>

namespace eosio::chain {

struct shared_finalizer_authority {
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
shared_finalizer_authority() = delete;
shared_finalizer_authority( const shared_finalizer_authority& ) = default;
shared_finalizer_authority( shared_finalizer_authority&& ) = default;
shared_finalizer_authority& operator= ( shared_finalizer_authority && ) = default;
shared_finalizer_authority& operator= ( const shared_finalizer_authority & ) = default;

shared_finalizer_authority( const std::string& description, const uint64_t fweight, const fc::crypto::blslib::bls_public_key& public_key )
:description(description)
,fweight(fweight)
,public_key(public_key)
{}

std::string description;
Copy link
Member

Choose a reason for hiding this comment

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

This needs to be shared_string

Copy link
Author

Choose a reason for hiding this comment

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

This breaks compilation (don't know why) so leaving for later

uint64_t fweight;
fc::crypto::blslib::bls_public_key public_key;
heifner marked this conversation as resolved.
Show resolved Hide resolved
};

struct shared_finalizer_set {
shared_finalizer_set() = delete;

explicit shared_finalizer_set( chainbase::allocator<char> alloc )
:finalizers(alloc){}

shared_finalizer_set( const shared_finalizer_set& ) = default;
shared_finalizer_set( shared_finalizer_set&& ) = default;
shared_finalizer_set& operator= ( shared_finalizer_set && ) = default;
shared_finalizer_set& operator= ( const shared_finalizer_set & ) = default;

uint32_t version = 0; ///< sequentially incrementing version number
uint64_t fthreshold = 0; // minimum finalizer fweight sum for block finalization
shared_vector<shared_finalizer_authority> finalizers;
};

struct finalizer_authority {

std::string description;
uint64_t fweight; // weight that this finalizer's vote has for meeting fthreshold
fc::crypto::blslib::bls_public_key public_key;

auto to_shared(chainbase::allocator<char> alloc) const {
return shared_finalizer_authority(description, fweight, public_key);
}

static auto from_shared( const shared_finalizer_authority& src ) {
finalizer_authority result;
result.description = src.description;
result.fweight = src.fweight;
result.public_key = src.public_key;
return result;
fcecin marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* ABI's for contracts expect variants to be serialized as a 2 entry array of
* [type-name, value].
*
* This is incompatible with standard FC rules for
* static_variants which produce
*
* [ordinal, value]
*
* this method produces an appropriate variant for contracts where the authority field
* is correctly formatted
*/
fc::variant get_abi_variant() const;
heifner marked this conversation as resolved.
Show resolved Hide resolved

friend bool operator == ( const finalizer_authority& lhs, const finalizer_authority& rhs ) {
return tie( lhs.description, lhs.fweight, lhs.public_key ) == tie( rhs.description, rhs.fweight, rhs.public_key );
}
friend bool operator != ( const finalizer_authority& lhs, const finalizer_authority& rhs ) {
return tie( lhs.description, lhs.fweight, lhs.public_key ) != tie( rhs.description, rhs.fweight, rhs.public_key );
fcecin marked this conversation as resolved.
Show resolved Hide resolved
}
};

struct finalizer_set {
finalizer_set() = default;

finalizer_set( uint32_t version, uint64_t fthreshold, std::initializer_list<finalizer_authority> finalizers )
:version(version)
,fthreshold(fthreshold)
,finalizers(finalizers)
{}

auto to_shared(chainbase::allocator<char> alloc) const {
auto result = shared_finalizer_set(alloc);
result.version = version;
result.fthreshold = fthreshold;
result.finalizers.clear();
result.finalizers.reserve( finalizers.size() );
for( const auto& f : finalizers ) {
result.finalizers.emplace_back(f.to_shared(alloc));
}
return result;
}

static auto from_shared( const shared_finalizer_set& src ) {
finalizer_set result;
result.version = src.version;
result.fthreshold = src.fthreshold;
result.finalizers.reserve( src.finalizers.size() );
for( const auto& f : src.finalizers ) {
result.finalizers.emplace_back(finalizer_authority::from_shared(f));
}
return result;
}

uint32_t version = 0; ///< sequentially incrementing version number
uint64_t fthreshold; // vote fweight threshold to finalize blocks
vector<finalizer_authority> finalizers; // Instant Finality voter set

friend bool operator == ( const finalizer_set& a, const finalizer_set& b )
{
if( a.version != b.version ) return false;
if( a.fthreshold != b.fthreshold ) return false;
if ( a.finalizers.size() != b.finalizers.size() ) return false;
for( uint32_t i = 0; i < a.finalizers.size(); ++i )
if( ! (a.finalizers[i] == b.finalizers[i]) ) return false;
return true;
}

friend bool operator != ( const finalizer_set& a, const finalizer_set& b )
{
return !(a==b);
}
};

} /// eosio::chain

FC_REFLECT( eosio::chain::finalizer_authority, (description)(fweight)(public_key) )
FC_REFLECT( eosio::chain::finalizer_set, (version)(fthreshold)(finalizers) )
FC_REFLECT( eosio::chain::shared_finalizer_authority, (description)(fweight)(public_key) )
FC_REFLECT( eosio::chain::shared_finalizer_set, (version)(fthreshold)(finalizers) )
9 changes: 9 additions & 0 deletions libraries/chain/include/eosio/chain/webassembly/interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,15 @@ namespace webassembly {
*/
int64_t set_proposed_producers_ex(uint64_t packed_producer_format, legacy_span<const char> packed_producer_schedule);

/**
* Submits a finalizer set change to Hotstuff.
*
* @ingroup privileged
*
* @param packed_finalizer_set - a serialized finalizer_set object.
*/
void set_finalizers(span<const char> packed_finalizer_set);

/**
* Retrieve the blockchain config parameters.
*
Expand Down
30 changes: 30 additions & 0 deletions libraries/chain/webassembly/privileged.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <eosio/chain/transaction_context.hpp>
#include <eosio/chain/resource_limits.hpp>
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/finalizer_set.hpp>

#include <fc/io/datastream.hpp>

Expand Down Expand Up @@ -150,6 +151,35 @@ namespace eosio { namespace chain { namespace webassembly {
}
}

void interface::set_finalizers(span<const char> packed_finalizer_set) {
EOS_ASSERT(!context.trx_context.is_read_only(), wasm_execution_error, "set_proposed_finalizers not allowed in a readonly transaction");
fc::datastream<const char*> ds( packed_finalizer_set.data(), packed_finalizer_set.size() );
finalizer_set finset;
fc::raw::unpack(ds, finset);
vector<finalizer_authority> & finalizers = finset.finalizers;

EOS_ASSERT( finalizers.size() <= config::max_finalizers, wasm_execution_error, "Finalizer set exceeds the maximum finalizer count for this chain" );
EOS_ASSERT( finalizers.size() > 0, wasm_execution_error, "Finalizer set cannot be empty" );

std::set<fc::crypto::blslib::bls_public_key> unique_finalizer_keys;
#warning REVIEW: Is checking for unique finalizer descriptions at all relevant?
Copy link
Member

Choose a reason for hiding this comment

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

@arhag ?

std::set<std::string> unique_finalizers;
uint64_t f_weight_sum = 0;

for (const auto& f: finalizers) {
EOS_ASSERT( f.public_key.valid(), wasm_execution_error, "Finalizer set includes an invalid key" );
f_weight_sum += f.fweight;
unique_finalizer_keys.insert(f.public_key);
unique_finalizers.insert(f.description);
}

EOS_ASSERT( finalizers.size() == unique_finalizers.size(), wasm_execution_error, "Duplicate finalizer description in finalizer set" );
EOS_ASSERT( finalizers.size() == unique_finalizer_keys.size(), wasm_execution_error, "Duplicate finalizer bls key in finalizer set" );
EOS_ASSERT( finset.fthreshold > f_weight_sum / 2, wasm_execution_error, "Finalizer set treshold cannot be met by finalizer weights" );

context.control.set_finalizers( finset.fthreshold, std::move(finalizers) );
}

uint32_t interface::get_blockchain_parameters_packed( legacy_span<char> packed_blockchain_parameters ) const {
auto& gpo = context.control.get_global_properties();

Expand Down
19 changes: 19 additions & 0 deletions libraries/hotstuff/chain_pacemaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,25 @@ namespace eosio { namespace hotstuff {
}

std::vector<name> chain_pacemaker::get_finalizers() {

#warning FIXME: Use new finalizer list in pacemaker/qc_chain.
// Every time qc_chain wants to know what the finalizers are, we get it from the controller, which
// is where it's currently stored.
//
// TODO:
// - solve threading. for this particular case, I don't think using _chain-> is a big deal really;
// set_finalizers is called once in a blue moon, and this could be solved by a simple mutex even
// if it is the main thread that is waiting for a lock. But maybe there's a better way to do this
// overall.
// - use this information in qc_chain and delete the old code below
// - list of string finalizer descriptions instead of eosio name now
// - also return the keys for each finalizer, not just name/description so qc_chain can use them
//
uint64_t fthreshold;
vector<finalizer_authority> finalizers;
_chain->get_finalizers(fthreshold, finalizers);

// Old code: get eosio::name from the producer schedule
const block_state_ptr& hbs = _chain->head_block_state();
const std::vector<producer_authority>& pa_list = hbs->active_schedule.producers;
std::vector<name> pn_list;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <eosio/hotstuff/base_pacemaker.hpp>
#include <eosio/hotstuff/qc_chain.hpp>

#include <eosio/chain/finalizer_set.hpp>

#include <shared_mutex>

namespace eosio::chain {
Expand Down Expand Up @@ -70,7 +72,6 @@ namespace eosio::hotstuff {

uint32_t _quorum_threshold = 15; //FIXME/TODO: calculate from schedule
fc::logger& _logger;

};

} // namespace eosio::hotstuff
7 changes: 4 additions & 3 deletions libraries/libfc/include/fc/crypto/bls_public_key.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <fc/reflect/variant.hpp>
#include <fc/static_variant.hpp>
#include <bls12-381/bls12-381.hpp>
#include <bls12-381/scalar.hpp>

namespace fc { namespace crypto { namespace blslib {

Expand Down Expand Up @@ -60,9 +61,9 @@ namespace fc { namespace crypto { namespace blslib {


friend std::ostream& operator<< (std::ostream& s, const bls_public_key& k);
//friend bool operator == ( const bls_public_key& p1, const bls_public_key& p2);
//friend bool operator != ( const bls_public_key& p1, const bls_public_key& p2);
//friend bool operator < ( const bls_public_key& p1, const bls_public_key& p2);
friend bool operator == ( const bls_public_key& p1, const bls_public_key& p2) { return p1._pkey.equal(p2._pkey); }
friend bool operator != ( const bls_public_key& p1, const bls_public_key& p2) { return !p1._pkey.equal(p2._pkey); }
friend bool operator < ( const bls_public_key& p1, const bls_public_key& p2) { return bls12_381::scalar::cmp(p1._pkey.x.d, p2._pkey.x.d) < 0; }
friend struct reflector<bls_public_key>;
friend class bls_private_key;
}; // bls_public_key
Expand Down
Loading