diff --git a/libraries/chain/block.cpp b/libraries/chain/block.cpp index 31885839db..14af857a88 100644 --- a/libraries/chain/block.cpp +++ b/libraries/chain/block.cpp @@ -20,6 +20,11 @@ namespace eosio { namespace chain { } } + void quorum_certificate_extension::reflector_init() { + static_assert( fc::raw::has_feature_reflector_init_on_unpacked_reflected_types, "quorum_certificate_extension expects FC to support reflector_init" ); + static_assert( extension_id() == 3, "extension id for quorum_certificate_extension must be 3" ); + } + flat_multimap signed_block::validate_and_extract_extensions()const { using decompose_t = block_extension_types::decompose_t; diff --git a/libraries/chain/include/eosio/chain/abi_serializer.hpp b/libraries/chain/include/eosio/chain/abi_serializer.hpp index 9c0d81fa68..9bdb22480b 100644 --- a/libraries/chain/include/eosio/chain/abi_serializer.hpp +++ b/libraries/chain/include/eosio/chain/abi_serializer.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -690,6 +691,12 @@ namespace impl { std::get(block_exts.lower_bound(additional_block_signatures_extension::extension_id())->second); mvo("additional_signatures", additional_signatures); } + auto qc_extension_count = block_exts.count(quorum_certificate_extension::extension_id()); + if ( qc_extension_count > 0) { + const auto& qc_extension = + std::get(block_exts.lower_bound(quorum_certificate_extension::extension_id())->second); + mvo("qc_extension", qc_extension); + } out(name, std::move(mvo)); } diff --git a/libraries/chain/include/eosio/chain/block.hpp b/libraries/chain/include/eosio/chain/block.hpp index ab06da5d42..6edd68f84a 100644 --- a/libraries/chain/include/eosio/chain/block.hpp +++ b/libraries/chain/include/eosio/chain/block.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace eosio { namespace chain { @@ -70,6 +71,25 @@ namespace eosio { namespace chain { vector signatures; }; + struct quorum_certificate_extension : fc::reflect_init { + static constexpr uint16_t extension_id() { return 3; } + static constexpr bool enforce_unique() { return true; } + + quorum_certificate_extension() = default; + + quorum_certificate_extension( const quorum_certificate& qc) + :qc( qc ) + {} + + quorum_certificate_extension( quorum_certificate&& qc) + :qc( std::move(qc) ) + {} + + void reflector_init(); + + quorum_certificate qc; + }; + namespace detail { template struct block_extension_types { @@ -79,7 +99,7 @@ namespace eosio { namespace chain { } using block_extension_types = detail::block_extension_types< - additional_block_signatures_extension + additional_block_signatures_extension, quorum_certificate_extension >; using block_extension = block_extension_types::block_extension_t; @@ -119,4 +139,5 @@ FC_REFLECT_ENUM( eosio::chain::transaction_receipt::status_enum, FC_REFLECT(eosio::chain::transaction_receipt_header, (status)(cpu_usage_us)(net_usage_words) ) FC_REFLECT_DERIVED(eosio::chain::transaction_receipt, (eosio::chain::transaction_receipt_header), (trx) ) FC_REFLECT(eosio::chain::additional_block_signatures_extension, (signatures)); +FC_REFLECT(eosio::chain::quorum_certificate_extension, (qc)); FC_REFLECT_DERIVED(eosio::chain::signed_block, (eosio::chain::signed_block_header), (transactions)(block_extensions) ) diff --git a/libraries/chain/include/eosio/chain/exceptions.hpp b/libraries/chain/include/eosio/chain/exceptions.hpp index 50c322ff00..22aa819574 100644 --- a/libraries/chain/include/eosio/chain/exceptions.hpp +++ b/libraries/chain/include/eosio/chain/exceptions.hpp @@ -256,7 +256,6 @@ namespace eosio { namespace chain { FC_DECLARE_DERIVED_EXCEPTION( ill_formed_additional_block_signatures_extension, block_validate_exception, 3030013, "Block includes an ill-formed additional block signature extension" ) - FC_DECLARE_DERIVED_EXCEPTION( transaction_exception, chain_exception, 3040000, "Transaction exception" ) diff --git a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp index fb8456af9b..b81a3c7308 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp @@ -243,3 +243,5 @@ FC_REFLECT(eosio::chain::hs_proposal_message, (proposal_id)(block_id)(parent_id) FC_REFLECT(eosio::chain::hs_new_view_message, (high_qc)); FC_REFLECT(eosio::chain::finalizer_state, (b_leaf)(b_lock)(b_exec)(b_finality_violation)(block_exec)(pending_proposal_block)(v_height)(high_qc)(current_qc)(schedule)(proposals)); FC_REFLECT(eosio::chain::hs_message, (msg)); +FC_REFLECT(eosio::chain::valid_quorum_certificate, (_proposal_id)(_proposal_digest)(_strong_votes)(_weak_votes)(_sig)); +FC_REFLECT(eosio::chain::quorum_certificate, (block_height)(qc)); diff --git a/libraries/libfc/include/fc/io/datastream.hpp b/libraries/libfc/include/fc/io/datastream.hpp index 88984a3d4f..b17279481a 100644 --- a/libraries/libfc/include/fc/io/datastream.hpp +++ b/libraries/libfc/include/fc/io/datastream.hpp @@ -295,6 +295,7 @@ inline datastream& operator>>(datastream& ds, uint8_t& d) { ds.read((char*)&d, sizeof(d) ); return ds; } + /* template inline datastream& operator<<(datastream& ds, const boost::multiprecision::number& n) { diff --git a/libraries/libfc/include/fc/io/raw.hpp b/libraries/libfc/include/fc/io/raw.hpp index cc4317f797..e24e1a52f4 100644 --- a/libraries/libfc/include/fc/io/raw.hpp +++ b/libraries/libfc/include/fc/io/raw.hpp @@ -17,6 +17,7 @@ #include #include +#include #include namespace fc { @@ -34,6 +35,8 @@ namespace fc { template void unpack( Stream& s, Int<256>& n ); template void pack( Stream& s, const boost::multiprecision::number& n ); template void unpack( Stream& s, boost::multiprecision::number& n ); + template void pack( Stream& s, const boost::dynamic_bitset& bs ); + template void unpack( Stream& s, boost::dynamic_bitset& bs ); template inline void pack( Stream& s, const Arg0& a0, const Args&... args ) { @@ -564,6 +567,34 @@ namespace fc { } } + template + inline void pack( Stream& s, const boost::dynamic_bitset& value ) { + const auto num_blocks = value.num_blocks(); + FC_ASSERT( num_blocks <= MAX_NUM_ARRAY_ELEMENTS ); + fc::raw::pack( s, unsigned_int((uint32_t)num_blocks) ); + + // convert bitset to a vector of blocks + std::vector blocks; + blocks.resize(num_blocks); + boost::to_block_range(value, blocks.begin()); + + // pack the blocks + for (const auto& b: blocks) { + fc::raw::pack( s, b ); + } + } + + template + inline void unpack( Stream& s, boost::dynamic_bitset& value ) { + unsigned_int size; fc::raw::unpack( s, size ); + FC_ASSERT( size.value <= MAX_NUM_ARRAY_ELEMENTS ); + std::vector blocks((size_t)size.value); + for( uint64_t i = 0; i < size.value; ++i ) { + fc::raw::unpack( s, blocks[i] ); + } + value = { blocks.cbegin(), blocks.cend() }; + } + template inline void pack( Stream& s, const std::vector& value ) { FC_ASSERT( value.size() <= MAX_NUM_ARRAY_ELEMENTS ); diff --git a/libraries/libfc/include/fc/variant_dynamic_bitset.hpp b/libraries/libfc/include/fc/variant_dynamic_bitset.hpp new file mode 100644 index 0000000000..9f337c5277 --- /dev/null +++ b/libraries/libfc/include/fc/variant_dynamic_bitset.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +namespace fc +{ + template void to_variant( const boost::dynamic_bitset& bs, fc::variant& v ) { + auto num_blocks = bs.num_blocks(); + if ( num_blocks > MAX_NUM_ARRAY_ELEMENTS ) throw std::range_error( "number of blocks of dynamic_bitset cannot be greather than MAX_NUM_ARRAY_ELEMENTS" ); + + std::vector blocks(num_blocks); + boost::to_block_range(bs, blocks.begin()); + + v = fc::variant(blocks); + } + + template void from_variant( const fc::variant& v, boost::dynamic_bitset& bs ) { + const std::vector& vars = v.get_array(); + auto num_vars = vars.size(); + if( num_vars > MAX_NUM_ARRAY_ELEMENTS ) throw std::range_error( "number of variants for dynamic_bitset cannot be greather than MAX_NUM_ARRAY_ELEMENTS" ); + + std::vector blocks; + blocks.reserve(num_vars); + for( const auto& var: vars ) { + blocks.push_back( var.as() ); + } + + bs = { blocks.cbegin(), blocks.cend() }; + } +} // namespace fc diff --git a/libraries/libfc/test/CMakeLists.txt b/libraries/libfc/test/CMakeLists.txt index 0c33b8fab9..4b079be425 100644 --- a/libraries/libfc/test/CMakeLists.txt +++ b/libraries/libfc/test/CMakeLists.txt @@ -8,11 +8,13 @@ add_executable( test_fc crypto/test_webauthn.cpp io/test_cfile.cpp io/test_json.cpp + io/test_raw.cpp io/test_tracked_storage.cpp network/test_message_buffer.cpp scoped_exit/test_scoped_exit.cpp static_variant/test_static_variant.cpp variant/test_variant.cpp + variant/test_variant_dynamic_bitset.cpp variant_estimated_size/test_variant_estimated_size.cpp test_base64.cpp test_escape_str.cpp diff --git a/libraries/libfc/test/io/test_raw.cpp b/libraries/libfc/test/io/test_raw.cpp new file mode 100644 index 0000000000..0ff902a97a --- /dev/null +++ b/libraries/libfc/test/io/test_raw.cpp @@ -0,0 +1,38 @@ +#include +#include + +#include +#include + +using namespace fc; + +BOOST_AUTO_TEST_SUITE(raw_test_suite) + + +BOOST_AUTO_TEST_CASE(dynamic_bitset_test) +{ + constexpr uint8_t bits = 0b00011110; + boost::dynamic_bitset bs1(8, bits); // bit set size 8 + + char buff[4]; + datastream ds(buff, sizeof(buff)); + + fc::raw::pack( ds, bs1 ); + + boost::dynamic_bitset bs2(8); + ds.seekp(0); + fc::raw::unpack( ds, bs2 ); + + // 0b00011110 + BOOST_CHECK(!bs2.test(0)); + BOOST_CHECK(bs2.test(1)); + BOOST_CHECK(bs2.test(2)); + BOOST_CHECK(bs2.test(2)); + BOOST_CHECK(bs2.test(3)); + BOOST_CHECK(bs2.test(4)); + BOOST_CHECK(!bs2.test(5)); + BOOST_CHECK(!bs2.test(6)); + BOOST_CHECK(!bs2.test(7)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libraries/libfc/test/variant/test_variant.cpp b/libraries/libfc/test/variant/test_variant.cpp index 8be5d99232..022f564e1b 100644 --- a/libraries/libfc/test/variant/test_variant.cpp +++ b/libraries/libfc/test/variant/test_variant.cpp @@ -1,8 +1,9 @@ -#include - #include #include #include + +#include + #include using namespace fc; diff --git a/libraries/libfc/test/variant/test_variant_dynamic_bitset.cpp b/libraries/libfc/test/variant/test_variant_dynamic_bitset.cpp new file mode 100644 index 0000000000..e03d9285ca --- /dev/null +++ b/libraries/libfc/test/variant/test_variant_dynamic_bitset.cpp @@ -0,0 +1,36 @@ +#include +#include +#include + +#include + +#include + +using namespace fc; +using std::string; + +BOOST_AUTO_TEST_SUITE(dynamic_bitset_test_suite) + +BOOST_AUTO_TEST_CASE(dynamic_bitset_test) +{ + constexpr uint8_t bits = 0b0000000001010100; + boost::dynamic_bitset bs(16, bits); // 2 blocks of uint8_t + + fc::mutable_variant_object mu; + mu("bs", bs); + + // a vector of 2 blocks + const variants& vars = mu["bs"].get_array(); + BOOST_CHECK_EQUAL(vars.size(), 2u); + + // blocks can be in any order + if (vars[0].as() == bits ) { + BOOST_CHECK_EQUAL(vars[1].as(), 0u); + } else if (vars[1].as() == bits ) { + BOOST_CHECK_EQUAL(vars[0].as(), 0u); + } else { + BOOST_CHECK(false); + } +} + +BOOST_AUTO_TEST_SUITE_END()