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: Change bls_public_key & bls_signature serialization #2338

Merged
merged 13 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 9 additions & 6 deletions libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,21 +192,24 @@ void block_state::verify_qc(const valid_quorum_certificate& qc) const {
("s", strong_weights)("w", weak_weights)("t", active_finalizer_policy->threshold) );
}

std::vector<bls_public_key> pubkeys;
// no reason to use bls_public_key wrapper
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
std::vector<bls12_381::g1> pubkeys;
pubkeys.reserve(2);
std::vector<std::vector<uint8_t>> digests;
digests.reserve(2);

// utility to aggregate public keys for verification
auto aggregate_pubkeys = [&](const auto& votes_bitset) -> bls_public_key {
auto aggregate_pubkeys = [&](const auto& votes_bitset) -> bls12_381::g1 {
const auto n = std::min(num_finalizers, votes_bitset.size());
std::vector<bls_public_key> pubkeys_to_aggregate;
std::vector<bls12_381::g1> pubkeys_to_aggregate;
pubkeys_to_aggregate.reserve(n);
for(auto i = 0u; i < n; ++i) {
if (votes_bitset[i]) { // ith finalizer voted
pubkeys_to_aggregate.emplace_back(finalizers[i].public_key);
pubkeys_to_aggregate.emplace_back(finalizers[i].public_key.jacobian_montgomery_le());
}
}

return fc::crypto::blslib::aggregate(pubkeys_to_aggregate);
return bls12_381::aggregate_public_keys(pubkeys_to_aggregate);
};

// aggregate public keys and digests for strong and weak votes
Expand All @@ -221,7 +224,7 @@ void block_state::verify_qc(const valid_quorum_certificate& qc) const {
}

// validate aggregated signature
EOS_ASSERT( fc::crypto::blslib::aggregate_verify( pubkeys, digests, qc._sig ),
EOS_ASSERT( bls12_381::aggregate_verify(pubkeys, digests, qc._sig.jacobian_montgomery_le()),
invalid_qc_claim, "signature validation failed" );
}

Expand Down
18 changes: 4 additions & 14 deletions libraries/chain/hotstuff/hotstuff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ vote_status pending_quorum_certificate::votes_t::add_vote(std::span<const uint8_
return vote_status::invalid_signature;
}
_bitset.set(index);
_sig = fc::crypto::blslib::aggregate(std::array{_sig, new_sig}); // works even if _sig is default initialized (fp2::zero())
_sig.aggregate(new_sig); // works even if _sig is default initialized (fp2::zero())
return vote_status::success;
}

void pending_quorum_certificate::votes_t::reset(size_t num_finalizers) {
if (num_finalizers != _bitset.size())
_bitset.resize(num_finalizers);
_bitset.reset();
_sig = bls_signature();
_sig = bls_aggregate_signature();
}

pending_quorum_certificate::pending_quorum_certificate()
Expand Down Expand Up @@ -149,7 +149,8 @@ valid_quorum_certificate pending_quorum_certificate::to_valid_quorum_certificate
} else if (is_quorum_met_no_lock()) {
valid_qc._strong_votes = _strong_votes._bitset;
valid_qc._weak_votes = _weak_votes._bitset;
valid_qc._sig = fc::crypto::blslib::aggregate(std::array{_strong_votes._sig, _weak_votes._sig});
valid_qc._sig = _strong_votes._sig;
valid_qc._sig.aggregate(_weak_votes._sig);
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
} else
assert(0); // this should be called only when we have a valid qc.

Expand All @@ -160,15 +161,4 @@ bool pending_quorum_certificate::is_quorum_met_no_lock() const {
return is_quorum_met(_state);
}

valid_quorum_certificate::valid_quorum_certificate(
const std::vector<uint32_t>& strong_votes, // bitset encoding, following canonical order
const std::vector<uint32_t>& weak_votes, // bitset encoding, following canonical order
const bls_signature& sig)
: _sig(sig) {
if (!strong_votes.empty())
_strong_votes = vector_to_bitset(strong_votes);
if (!weak_votes.empty())
_weak_votes = vector_to_bitset(weak_votes);
}

} // namespace eosio::chain
26 changes: 9 additions & 17 deletions libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@

namespace eosio::chain {

using bls_public_key = fc::crypto::blslib::bls_public_key;
using bls_signature = fc::crypto::blslib::bls_signature;
using bls_private_key = fc::crypto::blslib::bls_private_key;
using bls_public_key = fc::crypto::blslib::bls_public_key;
using bls_signature = fc::crypto::blslib::bls_signature;
using bls_aggregate_signature = fc::crypto::blslib::bls_aggregate_signature;
using bls_private_key = fc::crypto::blslib::bls_private_key;

using hs_bitset = boost::dynamic_bitset<uint32_t>;
using hs_bitset = boost::dynamic_bitset<uint32_t>;
using bls_key_map_t = std::map<bls_public_key, bls_private_key>;

struct vote_message {
Expand All @@ -39,22 +40,13 @@ namespace eosio::chain {
using bls_private_key = fc::crypto::blslib::bls_private_key;

// valid_quorum_certificate
class valid_quorum_certificate {
public:
valid_quorum_certificate(const std::vector<uint32_t>& strong_votes, //bitset encoding, following canonical order
const std::vector<uint32_t>& weak_votes, //bitset encoding, following canonical order
const bls_signature& sig);

valid_quorum_certificate() = default;
valid_quorum_certificate(const valid_quorum_certificate&) = default;

struct valid_quorum_certificate {
bool is_weak() const { return !!_weak_votes; }
bool is_strong() const { return !_weak_votes; }

friend struct fc::reflector<valid_quorum_certificate>;
std::optional<hs_bitset> _strong_votes;
std::optional<hs_bitset> _weak_votes;
bls_signature _sig;
bls_aggregate_signature _sig;
};

// quorum_certificate
Expand Down Expand Up @@ -87,8 +79,8 @@ namespace eosio::chain {
};

struct votes_t {
hs_bitset _bitset;
bls_signature _sig;
hs_bitset _bitset;
bls_aggregate_signature _sig;

void resize(size_t num_finalizers) { _bitset.resize(num_finalizers); }
size_t count() const { return _bitset.count(); }
Expand Down
11 changes: 4 additions & 7 deletions libraries/chain/webassembly/privileged.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ namespace eosio { namespace chain { namespace webassembly {
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;
std::set<fc::crypto::blslib::bls_public_key> unique_finalizer_keys;

uint64_t weight_sum = 0;

Expand All @@ -185,15 +185,12 @@ namespace eosio { namespace chain { namespace webassembly {
"Finalizer description greater than ${s}", ("s", config::max_finalizer_description_size) );
EOS_ASSERT(std::numeric_limits<uint64_t>::max() - weight_sum >= f.weight, wasm_execution_error, "sum of weights causes uint64_t overflow");
weight_sum += f.weight;
constexpr bool check = true; // always validate key
constexpr bool raw = false; // non-montgomery
EOS_ASSERT(f.public_key.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.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()) );
fc::crypto::blslib::bls_public_key pk(std::span<const uint8_t,96>(f.public_key.data(), 96));
EOS_ASSERT( unique_finalizer_keys.insert(pk).second, wasm_execution_error, "Duplicate public key: ${pk}", ("pk", pk.to_string()) );
finpol.finalizers.push_back(chain::finalizer_authority{.description = std::move(f.description),
.weight = f.weight,
.public_key{fc::crypto::blslib::bls_public_key{*pk}}});
.public_key{pk}});
}

EOS_ASSERT( weight_sum >= finpol.threshold && finpol.threshold > weight_sum / 2, wasm_execution_error, "Finalizer policy threshold (${t}) must be greater than half of the sum of the weights (${w}), and less than or equal to the sum of the weights", ("t", finpol.threshold)("w", weight_sum) );
Expand Down
9 changes: 2 additions & 7 deletions libraries/libfc/include/fc/crypto/bls_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@
namespace fc::crypto::blslib {

template <typename Container>
static Container deserialize_base64url(const std::string& data_str)
{

static Container deserialize_base64url(const std::string& data_str) {
using wrapper = checksummed_data<Container>;

wrapper wrapped;

auto bin = fc::base64url_decode(data_str);
Expand All @@ -22,10 +19,8 @@ namespace fc::crypto::blslib {
}

template <typename Container>
static std::string serialize_base64url( Container data) {

static std::string serialize_base64url(const Container& data) {
using wrapper = checksummed_data<Container>;

wrapper wrapped;

wrapped.data = data;
Expand Down
5 changes: 2 additions & 3 deletions libraries/libfc/include/fc/crypto/bls_private_key.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace fc::crypto::blslib {

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

std::string to_string(const yield_function_t& yield = yield_function_t()) const;
std::string to_string() const;
linh2931 marked this conversation as resolved.
Show resolved Hide resolved

bls_public_key get_public_key() const;

Expand All @@ -42,8 +42,7 @@ namespace fc::crypto::blslib {
} // fc::crypto::blslib

namespace fc {
void to_variant(const crypto::blslib::bls_private_key& var, variant& vo,
const yield_function_t& yield = yield_function_t());
void to_variant(const crypto::blslib::bls_private_key& var, variant& vo);

void from_variant(const variant& var, crypto::blslib::bls_private_key& vo);
} // namespace fc
Expand Down
89 changes: 65 additions & 24 deletions libraries/libfc/include/fc/crypto/bls_public_key.hpp
Original file line number Diff line number Diff line change
@@ -1,45 +1,86 @@
#pragma once
#include <fc/crypto/bls_signature.hpp>
#include <fc/reflect/reflect.hpp>
#include <fc/reflect/variant.hpp>
#include <fc/io/varint.hpp>
#include <fc/exception/exception.hpp>
#include <bls12-381/bls12-381.hpp>



namespace fc::crypto::blslib {

namespace config {
const std::string bls_public_key_prefix = "PUB_BLS_";
};

class bls_public_key
{
public:

bls_public_key() = default;
bls_public_key( bls_public_key&& ) = default;
bls_public_key( const bls_public_key& ) = default;
explicit bls_public_key( const bls12_381::g1& pkey ) {_pkey = pkey;}
// affine non-montgomery base64url with bls_public_key_prefix
explicit bls_public_key(const std::string& base64urlstr);
// Immutable after construction (although operator= is provided).
// Atributes are not const because FC_REFLECT only works for non-const members.
// Provides an efficient wrapper around bls12_381::g1.
// Serialization form:
// Non-Montgomery form and little-endian encoding for the field elements.
// Affine form for the group element (the z component is 1 and not included in the serialization).
// Binary serialization encodes size(96), x component, followed by y component.
// Cached g1 in Jacobian Montgomery is used for efficient BLS math.
// Keeping the serialized data allows for efficient serialization without the expensive conversion
// from Jacobian Montgomery to Affine Non-Montgomery.
class bls_public_key : fc::reflect_init {
public:
bls_public_key() = default;
bls_public_key(bls_public_key&&) = default;
bls_public_key(const bls_public_key&) = default;
bls_public_key& operator=(const bls_public_key& rhs) = default;
bls_public_key& operator=(bls_public_key&& rhs) = default;

// throws if unable to convert to valid bls12_381::g1
explicit bls_public_key(std::span<const uint8_t, 96> affine_non_montgomery_le);

// affine non-montgomery base64url with bls_public_key_prefix
explicit bls_public_key(const std::string& base64urlstr);

// affine non-montgomery base64url with bls_public_key_prefix
std::string to_string() const;

bls_public_key& operator=(const bls_public_key&) = default;
const bls12_381::g1& jacobian_montgomery_le() const { return _jacobian_montgomery_le; }
const std::array<uint8_t, 96>& affine_non_montgomery_le() const { return _affine_non_montgomery_le; }

// affine non-montgomery base64url with bls_public_key_prefix
std::string to_string(const yield_function_t& yield = yield_function_t()) const;
bool equal(const bls_public_key& pkey) const {
return _jacobian_montgomery_le.equal(pkey._jacobian_montgomery_le);
}

bool equal(const bls_public_key& pkey) const;
auto operator<=>(const bls_public_key&) const = default;
auto operator<=>(const bls_public_key& rhs) const {
return _affine_non_montgomery_le <=> rhs._affine_non_montgomery_le;
}
auto operator==(const bls_public_key& rhs) const {
return _affine_non_montgomery_le == rhs._affine_non_montgomery_le;
}

bls12_381::g1 _pkey;
template<typename T>
friend T& operator<<(T& ds, const bls_public_key& sig) {
// Serialization as variable length array when it is stored as a fixed length array. This makes for easier deserialization by external tools
fc::raw::pack(ds, fc::unsigned_int(static_cast<uint32_t>(sizeof(sig._affine_non_montgomery_le))));
ds.write(reinterpret_cast<const char*>(sig._affine_non_montgomery_le.data()), sizeof(sig._affine_non_montgomery_le));
return ds;
}

}; // bls_public_key
template<typename T>
friend T& operator>>(T& ds, bls_public_key& sig) {
// Serialization as variable length array when it is stored as a fixed length array. This makes for easier deserialization by external tools
fc::unsigned_int size;
fc::raw::unpack( ds, size );
FC_ASSERT(size.value == sizeof(sig._affine_non_montgomery_le));
ds.read(reinterpret_cast<char*>(sig._affine_non_montgomery_le.data()), sizeof(sig._affine_non_montgomery_le));
sig._jacobian_montgomery_le = from_affine_bytes_le(sig._affine_non_montgomery_le);
return ds;
}

static bls12_381::g1 from_affine_bytes_le(const std::array<uint8_t, 96>& affine_non_montgomery_le);
private:
std::array<uint8_t, 96> _affine_non_montgomery_le{};
bls12_381::g1 _jacobian_montgomery_le; // cached g1
};

} // fc::crypto::blslib

namespace fc {
void to_variant(const crypto::blslib::bls_public_key& var, variant& vo, const yield_function_t& yield = yield_function_t());

void to_variant(const crypto::blslib::bls_public_key& var, variant& vo);
void from_variant(const variant& var, crypto::blslib::bls_public_key& vo);
} // namespace fc

FC_REFLECT(bls12_381::g1, (x)(y)(z))
FC_REFLECT(crypto::blslib::bls_public_key, (_pkey) )
Loading
Loading