Skip to content

Commit

Permalink
Merge pull request #2338 from AntelopeIO/GH-2334-bls-serialization
Browse files Browse the repository at this point in the history
IF: Change bls_public_key & bls_signature serialization
  • Loading branch information
heifner authored Mar 28, 2024
2 parents f0038dc + aaa9284 commit 30fd771
Show file tree
Hide file tree
Showing 18 changed files with 373 additions and 296 deletions.
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
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);
} 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;

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

0 comments on commit 30fd771

Please sign in to comment.