Skip to content

Commit

Permalink
GH-2334 Change bls_public_key serialization format and optimize
Browse files Browse the repository at this point in the history
  • Loading branch information
heifner committed Mar 26, 2024
1 parent eb2fc59 commit 6bb9d36
Show file tree
Hide file tree
Showing 13 changed files with 116 additions and 121 deletions.
15 changes: 9 additions & 6 deletions libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,21 +187,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 @@ -216,7 +219,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._sig), //jacobian_montgomery_le()),
invalid_qc_claim, "signature validation failed" );
}

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
66 changes: 45 additions & 21 deletions libraries/libfc/include/fc/crypto/bls_public_key.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,67 @@
#include <fc/reflect/reflect.hpp>
#include <fc/reflect/variant.hpp>
#include <bls12-381/bls12-381.hpp>
#include <algorithm>

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.
// 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 x component first followed by y component.
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&) = default;
bls_public_key& operator=(bls_public_key&&) = default;

bls_public_key& operator=(const bls_public_key&) = default;
// throws if unable to convert to valie 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
std::string to_string(const yield_function_t& yield = yield_function_t()) const;
// affine non-montgomery base64url with bls_public_key_prefix
explicit bls_public_key(const std::string& base64urlstr);

bool equal(const bls_public_key& pkey) const;
auto operator<=>(const bls_public_key&) const = default;
// affine non-montgomery base64url with bls_public_key_prefix
std::string to_string() const;

bls12_381::g1 _pkey;
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; }

}; // bls_public_key
bool equal(const bls_public_key& pkey) const {
return _jacobian_montgomery_le.equal(pkey._jacobian_montgomery_le);
}

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;
}

private:
friend struct fc::reflector<bls_public_key>;
friend struct fc::reflector_init_visitor<bls_public_key>;
friend struct fc::has_reflector_init<bls_public_key>;
void reflector_init();

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) )
// Do not reflect cached g1, serialized form is Affine non-Montgomery little-endian
FC_REFLECT(crypto::blslib::bls_public_key, (_affine_non_montgomery_le) )
6 changes: 0 additions & 6 deletions libraries/libfc/include/fc/crypto/bls_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@ namespace fc::crypto::blslib {
std::span<const uint8_t> message,
const bls_signature& signature);

bls_public_key aggregate(std::span<const bls_public_key> keys);

bls_signature aggregate(std::span<const bls_signature> signatures);

bool aggregate_verify(std::span<const bls_public_key> pubkeys,
std::span<const std::vector<uint8_t>> messages,
const bls_signature& signature);

} // fc::crypto::blslib
9 changes: 5 additions & 4 deletions libraries/libfc/src/crypto/bls_private_key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ namespace fc::crypto::blslib {
bls_public_key bls_private_key::get_public_key() const
{
bls12_381::g1 pk = bls12_381::public_key(_sk);
return bls_public_key(pk);
constexpr bool raw = false;
return bls_public_key(pk.toAffineBytesLE(raw));
}

bls_signature bls_private_key::proof_of_possession() const
Expand Down Expand Up @@ -48,7 +49,7 @@ namespace fc::crypto::blslib {
:_sk(priv_parse_base64url(base64urlstr))
{}

std::string bls_private_key::to_string(const yield_function_t& yield) const
std::string bls_private_key::to_string() const
{
std::string data_str = fc::crypto::blslib::serialize_base64url<std::array<uint64_t, 4>>(_sk);

Expand All @@ -63,9 +64,9 @@ namespace fc::crypto::blslib {

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

void from_variant(const variant& var, crypto::blslib::bls_private_key& vo)
Expand Down
53 changes: 29 additions & 24 deletions libraries/libfc/src/crypto/bls_public_key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@

namespace fc::crypto::blslib {

static bls12_381::g1 pub_parse_base64url(const std::string& base64urlstr)
{
bls_public_key::bls_public_key(std::span<const uint8_t, 96> affine_non_montgomery_le) {
std::ranges::copy(affine_non_montgomery_le, _affine_non_montgomery_le.begin());
constexpr bool check = true; // verify
constexpr bool raw = false; // to montgomery
auto g1 = bls12_381::g1::fromAffineBytesLE(affine_non_montgomery_le, check, raw);
FC_ASSERT(g1, "Invalid bls_public_key");
_jacobian_montgomery_le = *g1;
}

static std::tuple<bls12_381::g1, std::array<uint8_t, 96>> pub_parse_base64url(const std::string& base64urlstr) {
auto res = std::mismatch(config::bls_public_key_prefix.begin(), config::bls_public_key_prefix.end(),
base64urlstr.begin());
FC_ASSERT(res.first == config::bls_public_key_prefix.end(), "BLS Public Key has invalid format : ${str}", ("str", base64urlstr));
Expand All @@ -19,40 +27,37 @@ namespace fc::crypto::blslib {
constexpr bool raw = false; // non-montgomery
std::optional<bls12_381::g1> g1 = bls12_381::g1::fromAffineBytesLE(bytes, check, raw);
FC_ASSERT(g1);
return *g1;
return {*g1, bytes};
}

bls_public_key::bls_public_key(const std::string& base64urlstr)
:_pkey(pub_parse_base64url(base64urlstr))
{}

std::string bls_public_key::to_string(const yield_function_t& yield)const {

constexpr bool raw = false; // non-montgomery
std::array<uint8_t, 96> bytes = _pkey.toAffineBytesLE(raw);

std::string data_str = fc::crypto::blslib::serialize_base64url<std::array<uint8_t, 96>>(bytes);
bls_public_key::bls_public_key(const std::string& base64urlstr) {
std::tie(_jacobian_montgomery_le, _affine_non_montgomery_le) = pub_parse_base64url(base64urlstr);
}

std::string bls_public_key::to_string() const {
std::string data_str = fc::crypto::blslib::serialize_base64url<std::array<uint8_t, 96>>(_affine_non_montgomery_le);
return config::bls_public_key_prefix + data_str;

}

bool bls_public_key::equal( const bls_public_key& pkey) const {
return _pkey.equal(pkey._pkey);
void bls_public_key::reflector_init() {
// called after construction, but always on the same thread and before bls_public_key passed to any other threads
static_assert(fc::raw::has_feature_reflector_init_on_unpacked_reflected_types,
"FC unpack needs to call reflector_init otherwise unpacked_trx will not be initialized");
std::optional<bls12_381::g1> g1 = bls12_381::g1::fromAffineBytesLE(_affine_non_montgomery_le);
FC_ASSERT(g1, "Invalid bls public key ${k}", ("k", _affine_non_montgomery_le));
_jacobian_montgomery_le = *g1;
}

} // fc::crypto::blslib

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

void to_variant(const crypto::blslib::bls_public_key& var, variant& vo) {
vo = var.to_string();
}

void from_variant(const variant& var, crypto::blslib::bls_public_key& vo)
{
void from_variant(const variant& var, crypto::blslib::bls_public_key& vo) {
vo = crypto::blslib::bls_public_key(var.as_string());
}

} // fc
24 changes: 1 addition & 23 deletions libraries/libfc/src/crypto/bls_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,7 @@ namespace fc::crypto::blslib {
bool verify(const bls_public_key& pubkey,
std::span<const uint8_t> message,
const bls_signature& signature) {
return bls12_381::verify(pubkey._pkey, message, signature._sig);
};

bls_public_key aggregate(std::span<const bls_public_key> keys) {
std::vector<bls12_381::g1> ks;
ks.reserve(keys.size());
for( const auto& k : keys ) {
ks.push_back(k._pkey);
}
bls12_381::g1 agg = bls12_381::aggregate_public_keys(ks);
return bls_public_key(agg);
return bls12_381::verify(pubkey.jacobian_montgomery_le(), message, signature._sig); //jacobian_montgomery_le());
};

bls_signature aggregate(std::span<const bls_signature> signatures) {
Expand All @@ -29,16 +19,4 @@ namespace fc::crypto::blslib {
return bls_signature{agg};
};

bool aggregate_verify(std::span<const bls_public_key> pubkeys,
std::span<const std::vector<uint8_t>> messages, // should be `std::span<const std::span<const uint8_t>>`
const bls_signature& signature) {
std::vector<bls12_381::g1> ks;
ks.reserve(pubkeys.size());
for( const auto& k : pubkeys ) {
ks.push_back(k._pkey);
}

return bls12_381::aggregate_verify(ks, messages, signature._sig);
};

} // fc::crypto::blslib
Loading

0 comments on commit 6bb9d36

Please sign in to comment.