Skip to content

Commit

Permalink
set_finalizers host function (#1511)
Browse files Browse the repository at this point in the history
Work in progress.

- Implemented host function (not tested);
- Finalizer set types;
- Placeholders for fc::crypto::blslib::bls_public_key operators == != and <;
- Stub notify_set_finalizers signal to be connected to chain_plugin, to reach chain_pacemaker
  • Loading branch information
fcecin committed Aug 25, 2023
1 parent aa926f0 commit dcb04cb
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 3 deletions.
8 changes: 8 additions & 0 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1990,6 +1990,10 @@ struct controller_impl {
emit( self.new_hs_new_block_message, msg );
}

void set_finalizers_impl(uint64_t fthreshold, vector<finalizer_authority> finalizers) {
emit( self.notify_set_finalizers, std::tie(fthreshold, finalizers) );
}

/**
* 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 +3317,10 @@ 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, finalizers);
}

const producer_authority_schedule& controller::active_producers()const {
if( !(my->pending) )
return my->head->active_schedule;
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
5 changes: 5 additions & 0 deletions 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>

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

int64_t set_proposed_producers( vector<producer_authority> producers );

void set_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 @@ -356,6 +360,7 @@ namespace eosio { namespace chain {
signal<void(const hs_vote_message_ptr&)> new_hs_vote_message;
signal<void(const hs_new_view_message_ptr&)> new_hs_new_view_message;
signal<void(const hs_new_block_message_ptr&)> new_hs_new_block_message;
signal<void(std::tuple<const uint64_t, const vector<finalizer_authority>&>)> notify_set_finalizers;

/*
signal<void()> pre_apply_block;
Expand Down
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 {
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 name& finalizer_name, const uint64_t fweight, const fc::crypto::blslib::bls_public_key& public_key )
:finalizer_name(finalizer_name)
,fweight(fweight)
,public_key(public_key)
{}

name finalizer_name;
uint64_t fweight;
fc::crypto::blslib::bls_public_key public_key;
};

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 {

name finalizer_name;
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(finalizer_name, fweight, public_key);
}

static auto from_shared( const shared_finalizer_authority& src ) {
finalizer_authority result;
result.finalizer_name = src.finalizer_name;
result.fweight = src.fweight;
result.public_key = src.public_key;
return result;
}

/**
* 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;

friend bool operator == ( const finalizer_authority& lhs, const finalizer_authority& rhs ) {
return tie( lhs.finalizer_name, lhs.fweight, lhs.public_key ) == tie( rhs.finalizer_name, rhs.fweight, rhs.public_key );
}
friend bool operator != ( const finalizer_authority& lhs, const finalizer_authority& rhs ) {
return tie( lhs.finalizer_name, lhs.fweight, lhs.public_key ) != tie( rhs.finalizer_name, rhs.fweight, rhs.public_key );
}
};

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, (finalizer_name)(fweight)(public_key) )
FC_REFLECT( eosio::chain::finalizer_set, (version)(fthreshold)(finalizers) )
FC_REFLECT( eosio::chain::shared_finalizer_authority, (finalizer_name)(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(legacy_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(legacy_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;
std::set<account_name> unique_finalizers;
uint64_t f_weight_sum = 0;

for (const auto& f: finalizers) {
EOS_ASSERT( context.is_account(f.finalizer_name), wasm_execution_error, "Finalizer set includes a nonexisting account" );
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.finalizer_name);
}

EOS_ASSERT( finalizers.size() == unique_finalizers.size(), wasm_execution_error, "Duplicate finalizer name 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
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 @@ -60,9 +60,10 @@ 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);
#warning FIXME/TODO: Must implement these operators.
friend bool operator == ( const bls_public_key& p1, const bls_public_key& p2) { return false; /*p1._pkey == p2._pkey;*/ }
friend bool operator != ( const bls_public_key& p1, const bls_public_key& p2) { return false; /*p1._pkey != p2._pkey;*/ }
friend bool operator < ( const bls_public_key& p1, const bls_public_key& p2) { return false; /*p1._pkey < p2._pkey;*/ }
friend struct reflector<bls_public_key>;
friend class bls_private_key;
}; // bls_public_key
Expand Down

0 comments on commit dcb04cb

Please sign in to comment.