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

IF: update libtester eosio.bios to match latest eosio.bios in reference-contracts, add more validation to set_finalizers host function #1989

Merged
merged 12 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from 10 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
4 changes: 2 additions & 2 deletions libraries/chain/hotstuff/qc_chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ namespace eosio::chain {
const auto& finalizers = _pacemaker->get_finalizer_policy().finalizers;

for (size_t i = 0; i < finalizers.size();i++) {
if (finalizers[i].public_key == finalizer_key) {
if ((finalizers[i].public_key <=> finalizer_key) == 0) {
heifner marked this conversation as resolved.
Show resolved Hide resolved
b.set(i);

fc_tlog(_logger, " === finalizer found ${finalizer} new value : ${value}",
Expand Down Expand Up @@ -358,7 +358,7 @@ namespace eosio::chain {
const auto& finalizers = _pacemaker->get_finalizer_policy().finalizers;
digest_type digest = p->get_proposal_digest();
for (size_t i=0; i<finalizers.size(); ++i) {
if (finalizers[i].public_key == vote.finalizer_key) {
if ((finalizers[i].public_key <=> vote.finalizer_key) == 0) {
if (_current_qc.add_vote(vote.strong, std::vector<uint8_t>(digest.data(), digest.data() + 32),
i, vote.finalizer_key, vote.sig)) {
// fc_tlog(_logger, " === update bitset ${value} ${finalizer_key}",
Expand Down
16 changes: 8 additions & 8 deletions libraries/chain/webassembly/privileged.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,32 +171,32 @@ namespace eosio { namespace chain { namespace webassembly {

std::vector<abi_finalizer_authority>& finalizers = abi_finpol.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" );
EOS_ASSERT( finalizers.size() <= config::max_finalizers, wasm_execution_error, "Finalizer policy exceeds the maximum finalizer count for this chain" );
EOS_ASSERT( finalizers.size() > 0, wasm_execution_error, "Finalizers cannot be empty" );

std::set<bls12_381::g1> unique_finalizer_keys;

uint64_t f_weight_sum = 0;

finalizer_policy finpol;
finpol.fthreshold = abi_finpol.fthreshold;
for (auto& f: finalizers) {
heifner marked this conversation as resolved.
Show resolved Hide resolved
EOS_ASSERT( f.description.size() <= config::max_finalizer_description_size, wasm_execution_error,
"Finalizer description greater than ${s}", ("s", config::max_finalizer_description_size) );
EOS_ASSERT(std::numeric_limits<uint64_t>::max() - f_weight_sum >= f.fweight, wasm_execution_error, "sum of weights causes uint64_t overflow");
f_weight_sum += f.fweight;
constexpr bool check = false; // system contract does proof of possession check which is a stronger check
constexpr bool check = true; // always validate key
constexpr bool raw = false; // non-montgomery
EOS_ASSERT(f.public_key_g1_affine_le.size() == 96, wasm_execution_error, "Invalid bls public key length");
std::optional<bls12_381::g1> pk = bls12_381::g1::fromAffineBytesLE(std::span<const uint8_t,96>(f.public_key_g1_affine_le.data(), 96), check, raw);
EOS_ASSERT( pk, wasm_execution_error, "Invalid public key for: ${d}", ("d", f.description) );
EOS_ASSERT( unique_finalizer_keys.insert(*pk).second, wasm_execution_error, "Duplicate public key: ${pk}", ("pk", fc::crypto::blslib::bls_public_key{*pk}.to_string()) );
finpol.finalizers.push_back(finalizer_authority{.description = std::move(f.description),
.fweight = f.fweight,
.public_key{fc::crypto::blslib::bls_public_key{*pk}}});
unique_finalizer_keys.insert(*pk);
}

// system contract should perform a duplicate check and fthreshold check before calling
EOS_ASSERT( finalizers.size() == unique_finalizer_keys.size(), wasm_execution_error, "Duplicate finalizer bls key in finalizer set" );
EOS_ASSERT( finpol.fthreshold > f_weight_sum / 2, wasm_execution_error, "Finalizer set threshold cannot be met by finalizer weights" );
EOS_ASSERT( finpol.fthreshold > f_weight_sum / 2, wasm_execution_error, "Finalizer policy threshold cannot be met by finalizer weights" );

context.control.set_proposed_finalizers( finpol );
}
Expand Down Expand Up @@ -277,4 +277,4 @@ namespace eosio { namespace chain { namespace webassembly {
}}} // ns eosio::chain::webassembly

FC_REFLECT(eosio::chain::webassembly::abi_finalizer_authority, (description)(fweight)(public_key_g1_affine_le));
heifner marked this conversation as resolved.
Show resolved Hide resolved
FC_REFLECT(eosio::chain::webassembly::abi_finalizer_policy, (fthreshold)(finalizers));
FC_REFLECT(eosio::chain::webassembly::abi_finalizer_policy, (fthreshold)(finalizers));
2 changes: 1 addition & 1 deletion libraries/libfc/include/fc/crypto/bls_public_key.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace fc::crypto::blslib {
// affine non-montgomery base64 with bls_public_key_prefix
std::string to_string(const yield_function_t& yield = yield_function_t()) const;

friend bool operator==(const bls_public_key& p1, const bls_public_key& p2);
bool equal(const bls_public_key& pkey) const;
auto operator<=>(const bls_public_key&) const = default;

bls12_381::g1 _pkey;
Expand Down
4 changes: 2 additions & 2 deletions libraries/libfc/include/fc/crypto/bls_signature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace fc::crypto::blslib {
// affine non-montgomery base64 with bls_signature_prefix
std::string to_string(const yield_function_t& yield = yield_function_t()) const;

friend bool operator == ( const bls_signature& p1, const bls_signature& p2);
bool equal( const bls_signature& sig ) const;

bls12_381::g2 _sig;

Expand All @@ -48,4 +48,4 @@ namespace fc {
FC_REFLECT(bls12_381::fp, (d))
FC_REFLECT(bls12_381::fp2, (c0)(c1))
FC_REFLECT(bls12_381::g2, (x)(y)(z))
FC_REFLECT(crypto::blslib::bls_signature, (_sig) )
FC_REFLECT(crypto::blslib::bls_signature, (_sig) )
4 changes: 2 additions & 2 deletions libraries/libfc/src/crypto/bls_public_key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ namespace fc::crypto::blslib {

}

bool operator == ( const bls_public_key& p1, const bls_public_key& p2) {
return p1._pkey.equal(p2._pkey);
bool bls_public_key::equal( const bls_public_key& pkey) const {
return _pkey.equal(pkey._pkey);
}

} // fc::crypto::blslib
Expand Down
4 changes: 2 additions & 2 deletions libraries/libfc/src/crypto/bls_signature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ namespace fc::crypto::blslib {

}

bool operator == ( const bls_signature& p1, const bls_signature& p2) {
return p1._sig.equal(p2._sig);
bool bls_signature::equal( const bls_signature& sig) const {
return _sig.equal(sig._sig);
}

} // fc::crypto::blslib
Expand Down
4 changes: 2 additions & 2 deletions libraries/libfc/test/test_bls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,15 +249,15 @@ BOOST_AUTO_TEST_CASE(bls_binary_keys_encoding_check) try {

bls_public_key pk = sk.get_public_key();

bool ok3 = bls_public_key(pk.to_string()) == pk;
bool ok3 = bls_public_key(pk.to_string()).equal(pk);

std::string pub_str = pk.to_string();

bool ok4 = bls_public_key(pub_str).to_string() == pub_str;

bls_signature sig = sk.sign(message_1);

bool ok5 = bls_signature(sig.to_string()) == sig;
bool ok5 = bls_signature(sig.to_string()).equal(sig);

std::string sig_str = sig.to_string();

Expand Down
24 changes: 14 additions & 10 deletions libraries/testing/contracts/eosio.bios/eosio.bios.abi
Original file line number Diff line number Diff line change
Expand Up @@ -179,21 +179,25 @@
"type": "string"
},
{
"name": "fweight",
"name": "weight",
"type": "uint64"
},
{
"name": "public_key_g1_affine_le",
"type": "bytes"
"name": "public_key",
"type": "string"
},
{
"name": "pop",
"type": "string"
}
]
},
{
"name": "finalizer_set",
"name": "finalizer_policy",
"base": "",
"fields": [
{
"name": "fthreshold",
"name": "threshold",
"type": "uint64"
},
{
Expand Down Expand Up @@ -395,12 +399,12 @@
]
},
{
"name": "setfinset",
"name": "setfinalizer",
"base": "",
"fields": [
{
"name": "fin_set",
"type": "finalizer_set"
"name": "finalizer_policy",
"type": "finalizer_policy"
}
]
},
Expand Down Expand Up @@ -550,8 +554,8 @@
"ricardian_contract": ""
},
{
"name": "setfinset",
"type": "setfinset",
"name": "setfinalizer",
"type": "setfinalizer",
"ricardian_contract": ""
},
{
Expand Down
80 changes: 66 additions & 14 deletions libraries/testing/contracts/eosio.bios/eosio.bios.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
#include "eosio.bios.hpp"

extern "C" {
__attribute__((eosio_wasm_import))
void set_finalizers( void* params, size_t params_size );
};
#include <eosio/instant_finality.hpp>
#include <unordered_set>

namespace eosiobios {

Expand All @@ -22,6 +19,69 @@ 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::abi_finalizer_policy abi_finalizer_policy;
abi_finalizer_policy.fthreshold = finalizer_policy.threshold;
abi_finalizer_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());
abi_finalizer_policy.finalizers.emplace_back(eosio::abi_finalizer_authority{f.description, f.weight, std::move(pk_vector)});
}

check(finalizer_policy.threshold > weight_sum / 2, "finalizer policy threshold must be greater than half of the sum of the weights");

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

void bios::onerror( ignore<uint128_t>, ignore<std::vector<char>> ) {
check( false, "the onerror action cannot be called directly" );
}
Expand All @@ -41,14 +101,6 @@ void bios::setprods( const std::vector<eosio::producer_authority>& schedule ) {
set_proposed_producers( schedule );
}

void bios::setfinset( const finalizer_set& fin_set ) {
require_auth( get_self() );

// until CDT provides a set_finalizers
auto packed_fin_set = eosio::pack( fin_set );
set_finalizers((void*)packed_fin_set.data(), packed_fin_set.size());
}

void bios::setparams( const eosio::blockchain_parameters& params ) {
require_auth( get_self() );
set_blockchain_parameters( params );
Expand All @@ -68,4 +120,4 @@ void bios::reqactivated( const eosio::checksum256& feature_digest ) {
check( is_feature_activated( feature_digest ), "protocol feature is not activated" );
}

}
}
41 changes: 19 additions & 22 deletions libraries/testing/contracts/eosio.bios/eosio.bios.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,32 +125,32 @@ namespace eosiobios {
(schedule_version)(new_producers))
};

/**
* finalizer_authority
*
* The public bls key of the hotstuff finalizer in Affine little-endian form.
*/
struct finalizer_authority {
std::string description;
uint64_t fweight = 0; // weight that this finalizer's vote has for meeting fthreshold
std::vector<uint8_t> public_key_g1_affine_le; // size 96, CDT/abi_serializer has issues with std::array
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
heifner marked this conversation as resolved.
Show resolved Hide resolved

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

constexpr size_t max_finalizers = 64*1024;
constexpr size_t max_finalizer_description_size = 256;

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

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


/**
* @defgroup eosiobios eosio.bios
* @ingroup eosiocontracts
Expand Down Expand Up @@ -344,16 +344,13 @@ namespace eosiobios {
void setprods( const std::vector<eosio::producer_authority>& schedule );

/**
* Set a new list of finalizers.
*
* @details Set a new list of active finalizers, by proposing a finalizer set, once the block that
* contains the proposal becomes irreversible the new finalizer set will be made active according
* to Antelope finalizer active set rules. Replaces existing finalizer set.
* Propose new finalizer policy that, unless superseded by a later
* finalizer policy, will eventually become the active finalizer policy.
*
* @param fin_et - New list of active finalizers to set
* @param finalizer_policy - proposed finalizer policy
*/
[[eosio::action]]
void setfinset( const finalizer_set& fin_set );
void setfinalizer( const finalizer_policy& finalizer_policy );

/**
* Set the blockchain parameters
Expand Down Expand Up @@ -425,7 +422,7 @@ namespace eosiobios {
using setpriv_action = action_wrapper<"setpriv"_n, &bios::setpriv>;
using setalimits_action = action_wrapper<"setalimits"_n, &bios::setalimits>;
using setprods_action = action_wrapper<"setprods"_n, &bios::setprods>;
using setfinset_action = action_wrapper<"setfinset"_n, &bios::setfinset>;
using setfinalizer_action = action_wrapper<"setfinalizer"_n, &bios::setfinalizer>;
using setparams_action = action_wrapper<"setparams"_n, &bios::setparams>;
using reqauth_action = action_wrapper<"reqauth"_n, &bios::reqauth>;
using activate_action = action_wrapper<"activate"_n, &bios::activate>;
Expand Down
Binary file modified libraries/testing/contracts/eosio.bios/eosio.bios.wasm
Binary file not shown.
Loading