Skip to content

Commit

Permalink
Merge pull request #43 from AntelopeIO/instant-finality
Browse files Browse the repository at this point in the history
IF: Instant finality updates
  • Loading branch information
arhag authored Apr 29, 2024
2 parents 732fa98 + 34f6854 commit c8f1317
Show file tree
Hide file tree
Showing 13 changed files with 1,605 additions and 30 deletions.
4 changes: 2 additions & 2 deletions .cicd/defaults.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"leap-dev":{
"target":"5",
"target":"hotstuff_integration",
"prerelease":false
},
"cdt":{
"target":"3",
"target":"hotstuff_integration",
"prerelease":false
}
}
1 change: 1 addition & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ jobs:
container-package: experimental-binaries
- name: Install packages
run: |
sudo apt-get update && sudo apt-get upgrade -y
sudo apt install ./*.deb
sudo apt-get install cmake
rm ./*.deb
Expand Down
42 changes: 42 additions & 0 deletions contracts/eosio.bios/include/eosio.bios/eosio.bios.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <eosio/eosio.hpp>
#include <eosio/fixed_bytes.hpp>
#include <eosio/privileged.hpp>
#include <eosio/instant_finality.hpp>
#include <eosio/producer_schedule.hpp>

namespace eosiobios {
Expand Down Expand Up @@ -66,6 +67,38 @@ namespace eosiobios {
(schedule_version)(new_producers))
};

/**
* finalizer_authority
*
* The public bls key and proof of possession of private key signature,
* and vote weight of a finalizer.
*/
constexpr size_t max_finalizers = 64*1024;
constexpr size_t max_finalizer_description_size = 256;

struct finalizer_authority {
std::string description;
uint64_t weight = 0; // weight that this finalizer's vote has for meeting threshold
std::string public_key; // public key of the finalizer in base64 format
std::string pop; // proof of possession of private key in base64 format

// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE(finalizer_authority, (description)(weight)(public_key)(pop))
};

/**
* finalizer_policy
*
* List of finalizer authorties along with the threshold
*/
struct finalizer_policy {
uint64_t threshold = 0; // quorum threshold
std::vector<finalizer_authority> finalizers;

// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE(finalizer_policy, (threshold)(finalizers));
};

/**
* The `eosio.bios` is the first sample of system contract provided by `block.one` through the EOSIO platform. It is a minimalist system contract because it only supplies the actions that are absolutely critical to bootstrap a chain and nothing more. This allows for a chain agnostic approach to bootstrapping a chain.
*
Expand Down Expand Up @@ -190,6 +223,15 @@ namespace eosiobios {
[[eosio::action]]
void onerror( ignore<uint128_t> sender_id, ignore<std::vector<char>> sent_trx );

/**
* Propose new finalizer policy that, unless superseded by a later
* finalizer policy, will eventually become the active finalizer policy.
*
* @param finalizer_policy - proposed finalizer policy
*/
[[eosio::action]]
void setfinalizer( const finalizer_policy& finalizer_policy );

/**
* Set privilege action allows to set privilege status for an account (turn it on/off).
* @param account - the account to set the privileged status for.
Expand Down
67 changes: 67 additions & 0 deletions contracts/eosio.bios/src/eosio.bios.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#include <eosio.bios/eosio.bios.hpp>
#include <eosio/crypto_bls_ext.hpp>

#include <unordered_set>

namespace eosiobios {

Expand All @@ -17,6 +20,70 @@ void bios::setabi( name account, const std::vector<char>& abi ) {
}
}

void bios::setfinalizer( const finalizer_policy& finalizer_policy ) {
// exensive checks are performed to make sure setfinalizer host function
// will never fail

require_auth( get_self() );

check(finalizer_policy.finalizers.size() <= max_finalizers, "number of finalizers exceeds the maximum allowed");
check(finalizer_policy.finalizers.size() > 0, "require at least one finalizer");

eosio::finalizer_policy fin_policy;
fin_policy.threshold = finalizer_policy.threshold;
fin_policy.finalizers.reserve(finalizer_policy.finalizers.size());

const std::string pk_prefix = "PUB_BLS";
const std::string sig_prefix = "SIG_BLS";

// use raw affine format (bls_g1 is std::array<char, 96>) for uniqueness check
struct g1_hash {
std::size_t operator()(const eosio::bls_g1& g1) const {
std::hash<const char*> hash_func;
return hash_func(g1.data());
}
};
struct g1_equal {
bool operator()(const eosio::bls_g1& lhs, const eosio::bls_g1& rhs) const {
return std::memcmp(lhs.data(), rhs.data(), lhs.size()) == 0;
}
};
std::unordered_set<eosio::bls_g1, g1_hash, g1_equal> unique_finalizer_keys;

uint64_t weight_sum = 0;

for (const auto& f: finalizer_policy.finalizers) {
check(f.description.size() <= max_finalizer_description_size, "Finalizer description greater than max allowed size");

// basic key format checks
check(f.public_key.substr(0, pk_prefix.length()) == pk_prefix, "public key shoud start with PUB_BLS");
check(f.pop.substr(0, sig_prefix.length()) == sig_prefix, "proof of possession signature should start with SIG_BLS");

// check overflow
check(std::numeric_limits<uint64_t>::max() - weight_sum >= f.weight, "sum of weights causes uint64_t overflow");
weight_sum += f.weight;

// decode_bls_public_key_to_g1 will fail ("check" function fails)
// if the key is invalid
const auto pk = eosio::decode_bls_public_key_to_g1(f.public_key);
// duplicate key check
check(unique_finalizer_keys.insert(pk).second, "duplicate public key");

const auto signature = eosio::decode_bls_signature_to_g2(f.pop);

// proof of possession of private key check
check(eosio::bls_pop_verify(pk, signature), "proof of possession failed");

std::vector<char> pk_vector(pk.begin(), pk.end());
fin_policy.finalizers.emplace_back(eosio::finalizer_authority{f.description, f.weight, std::move(pk_vector)});
}

check( weight_sum >= finalizer_policy.threshold && finalizer_policy.threshold > weight_sum / 2,
"Finalizer policy threshold must be greater than half of the sum of the weights, and less than or equal to the sum of the weights");

set_finalizers(std::move(fin_policy));
}

void bios::onerror( ignore<uint128_t>, ignore<std::vector<char>> ) {
check( false, "the onerror action cannot be called directly" );
}
Expand Down
1 change: 1 addition & 0 deletions contracts/eosio.system/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ add_contract(
${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.system.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/delegate_bandwidth.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/exchange_state.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/finalizer_key.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/name_bidding.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/native.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/producer_pay.cpp
Expand Down
157 changes: 156 additions & 1 deletion contracts/eosio.system/include/eosio.system/eosio.system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <eosio/singleton.hpp>
#include <eosio/system.hpp>
#include <eosio/time.hpp>
#include <eosio/instant_finality.hpp>

#include <eosio.system/exchange_state.hpp>
#include <eosio.system/native.hpp>
Expand All @@ -15,6 +16,7 @@
#include <optional>
#include <string>
#include <type_traits>
#include <unordered_set>

#ifdef CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX
#undef CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX
Expand Down Expand Up @@ -197,6 +199,8 @@ namespace eosiosystem {
EOSLIB_SERIALIZE( eosio_global_state4, (continuous_rate)(inflation_pay_factor)(votepay_factor) )
};



inline eosio::block_signing_authority convert_to_block_signing_authority( const eosio::public_key& producer_key ) {
return eosio::block_signing_authority_v0{ .threshold = 1, .keys = {{producer_key, 1}} };
}
Expand Down Expand Up @@ -282,6 +286,84 @@ namespace eosiosystem {
EOSLIB_SERIALIZE( producer_info2, (owner)(votepay_share)(last_votepay_share_update) )
};

// finalizer_key_info stores information about a finalizer key.
struct [[eosio::table("finkeys"), eosio::contract("eosio.system")]] finalizer_key_info {
uint64_t id; // automatically generated ID for the key in the table
name finalizer_name; // name of the finalizer owning the key
std::string finalizer_key; // finalizer key in base64url format
std::vector<char> finalizer_key_binary; // finalizer key in binary format in Affine little endian non-montgomery g1

uint64_t primary_key() const { return id; }
uint64_t by_fin_name() const { return finalizer_name.value; }
// Use binary format to hash. It is more robust and less likely to change
// than the base64url text encoding of it.
// There is no need to store the hash key to avoid re-computation,
// which only happens if the table row is modified. There won't be any
// modification of the table rows of; it may only be removed.
checksum256 by_fin_key() const { return eosio::sha256(finalizer_key_binary.data(), finalizer_key_binary.size()); }

bool is_active(uint64_t finalizer_active_key_id) const { return id == finalizer_active_key_id ; }
};
typedef eosio::multi_index<
"finkeys"_n, finalizer_key_info,
indexed_by<"byfinname"_n, const_mem_fun<finalizer_key_info, uint64_t, &finalizer_key_info::by_fin_name>>,
indexed_by<"byfinkey"_n, const_mem_fun<finalizer_key_info, checksum256, &finalizer_key_info::by_fin_key>>
> finalizer_keys_table;

// finalizer_info stores information about a finalizer.
struct [[eosio::table("finalizers"), eosio::contract("eosio.system")]] finalizer_info {
name finalizer_name; // finalizer's name
uint64_t active_key_id; // finalizer's active finalizer key's id in finalizer_keys_table, for fast finding key information
std::vector<char> active_key_binary; // finalizer's active finalizer key's binary format in Affine little endian non-montgomery g1
uint32_t finalizer_key_count = 0; // number of finalizer keys registered by this finalizer

uint64_t primary_key() const { return finalizer_name.value; }
};
typedef eosio::multi_index< "finalizers"_n, finalizer_info > finalizers_table;

// finalizer_auth_info stores a finalizer's key id and its finalizer authority
struct finalizer_auth_info {
finalizer_auth_info() = default;
explicit finalizer_auth_info(const finalizer_info& finalizer);

uint64_t key_id; // A finalizer's key ID in finalizer_keys_table
eosio::finalizer_authority fin_authority; // The finalizer's finalizer_authority

bool operator==(const finalizer_auth_info& other) const {
// Weight and description can never be changed by a user.
// They are not considered here.
return key_id == other.key_id &&
fin_authority.public_key == other.fin_authority.public_key;
};

EOSLIB_SERIALIZE( finalizer_auth_info, (key_id)(fin_authority) )
};

// A single entry storing information about last proposed finalizers.
// Should avoid using the global singleton pattern as it unnecessarily
// serializes data at construction/desstruction of system_contract,
// even if the data is not used.
struct [[eosio::table("lastpropfins"), eosio::contract("eosio.system")]] last_prop_finalizers_info {
std::vector<finalizer_auth_info> last_proposed_finalizers; // sorted by ascending finalizer key id

uint64_t primary_key()const { return 0; }

EOSLIB_SERIALIZE( last_prop_finalizers_info, (last_proposed_finalizers) )
};

typedef eosio::multi_index< "lastpropfins"_n, last_prop_finalizers_info > last_prop_fins_table;

// A single entry storing next available finalizer key_id to make sure
// key_id in finalizers_table will never be reused.
struct [[eosio::table("finkeyidgen"), eosio::contract("eosio.system")]] fin_key_id_generator_info {
uint64_t next_finalizer_key_id = 0;
uint64_t primary_key()const { return 0; }

EOSLIB_SERIALIZE( fin_key_id_generator_info, (next_finalizer_key_id) )
};

typedef eosio::multi_index< "finkeyidgen"_n, fin_key_id_generator_info > fin_key_id_gen_table;

// Voter info. Voter info stores information about the voter:
// - `owner` the voter
// - `proxy` the proxy set by the voter, if any
Expand Down Expand Up @@ -329,7 +411,6 @@ namespace eosiosystem {

typedef eosio::multi_index< "producers2"_n, producer_info2 > producers_table2;


typedef eosio::singleton< "global"_n, eosio_global_state > global_state_singleton;

typedef eosio::singleton< "global2"_n, eosio_global_state2 > global_state2_singleton;
Expand Down Expand Up @@ -705,6 +786,11 @@ namespace eosiosystem {
voters_table _voters;
producers_table _producers;
producers_table2 _producers2;
finalizer_keys_table _finalizer_keys;
finalizers_table _finalizers;
last_prop_fins_table _last_prop_finalizers;
std::optional<std::vector<finalizer_auth_info>> _last_prop_finalizers_cached;
fin_key_id_gen_table _fin_key_id_generator;
global_state_singleton _global;
global_state2_singleton _global2;
global_state3_singleton _global3;
Expand Down Expand Up @@ -1260,6 +1346,68 @@ namespace eosiosystem {
[[eosio::action]]
void unregprod( const name& producer );

/**
* Action to permanently transition to Savanna consensus.
* Create the first generation of finalizer policy and activate
* the policy by using `set_finalizers` host function
*
* @pre Require the authority of the contract itself
* @pre A sufficient numner of the top 21 block producers have registered a finalizer key
*/
[[eosio::action]]
void switchtosvnn();

/**
* Action to register a finalizer key by a registered producer.
* If this was registered before (and still exists) even
* by other block producers, the registration will fail.
* If this is the first registered finalizer key of the producer,
* it will also implicitly be marked active.
* A registered producer can have multiple registered finalizer keys.
*
* @param finalizer_name - account registering `finalizer_key`,
* @param finalizer_key - key to be registered. The key is in base64url format.
* @param proof_of_possession - a valid Proof of Possession signature to show the producer owns the private key of the finalizer_key. The signature is in base64url format.
*
* @pre `finalizer_name` must be a registered producer
* @pre `finalizer_key` must be in base64url format
* @pre `proof_of_possession` must be a valid of proof of possession signature
* @pre Authority of `finalizer_name` to register. `linkauth` may be used to allow a lower authrity to exectute this action.
*/
[[eosio::action]]
void regfinkey( const name& finalizer_name, const std::string& finalizer_key, const std::string& proof_of_possession);

/**
* Action to activate a finalizer key. If the finalizer is currently an
* active block producer (in top 21), then immediately change Finalizer Policy.
* A finalizer may only have one active finalizer key. Activating a
* finalizer key implicitly deactivates the previously active finalizer
* key of that finalizer.
*
* @param finalizer_name - account activating `finalizer_key`,
* @param finalizer_key - key to be activated.
*
* @pre `finalizer_key` must be a registered finalizer key in base64url format
* @pre Authority of `finalizer_name`
*/
[[eosio::action]]
void actfinkey( const name& finalizer_name, const std::string& finalizer_key );

/**
* Action to delete a finalizer key. An active finalizer key may not be deleted
* unless it is the last registered finalizer key. If it is the last one,
* it will be deleted.
*
* @param finalizer_name - account deleting `finalizer_key`,
* @param finalizer_key - key to be deleted.
*
* @pre `finalizer_key` must be a registered finalizer key in base64url format
* @pre `finalizer_key` must not be active, unless it is the last registered finalizer key
* @pre Authority of `finalizer_name`
*/
[[eosio::action]]
void delfinkey( const name& finalizer_name, const std::string& finalizer_key );

/**
* Set ram action sets the ram supply.
* @param max_ram_size - the amount of ram supply to set.
Expand Down Expand Up @@ -1604,6 +1752,13 @@ namespace eosiosystem {
double update_total_votepay_share( const time_point& ct,
double additional_shares_delta = 0.0, double shares_rate_delta = 0.0 );

// defined in finalizer_key.cpp
bool is_savanna_consensus();
void set_proposed_finalizers( std::vector<finalizer_auth_info> finalizers );
const std::vector<finalizer_auth_info>& get_last_proposed_finalizers();
uint64_t get_next_finalizer_key_id();
finalizers_table::const_iterator get_finalizer_itr( const name& finalizer_name ) const;

template <auto system_contract::*...Ptrs>
class registration {
public:
Expand Down
4 changes: 4 additions & 0 deletions contracts/eosio.system/src/eosio.system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ namespace eosiosystem {
_voters(get_self(), get_self().value),
_producers(get_self(), get_self().value),
_producers2(get_self(), get_self().value),
_finalizer_keys(get_self(), get_self().value),
_finalizers(get_self(), get_self().value),
_last_prop_finalizers(get_self(), get_self().value),
_fin_key_id_generator(get_self(), get_self().value),
_global(get_self(), get_self().value),
_global2(get_self(), get_self().value),
_global3(get_self(), get_self().value),
Expand Down
Loading

0 comments on commit c8f1317

Please sign in to comment.