diff --git a/include/ack/fe.hpp b/include/ack/fe.hpp index ec3ebb4..80604dc 100644 --- a/include/ack/fe.hpp +++ b/include/ack/fe.hpp @@ -37,6 +37,15 @@ namespace ack { return Derived::one(); } + /** + * Returns the max element bytes size. + * @return max element size. + */ + constexpr std::size_t max_byte_length() const + { + return underlying().max_byte_length(); + } + /** * Casts this element to big integer. * @return const reference to big integer representation of this element. @@ -82,6 +91,30 @@ namespace ack { return underlying().is_negative(); } + /** + * Returns the byte-encoded representation of this object. + * + * @param len - The desired length of the byte-encoded representation + * The full byte-encoded representation is returned if len is smaller + * than the byte length of the original object. + * + * @return The byte-encoded representation of this object. + */ + bytes to_bytes(std::size_t len) const + { + return underlying().to_bytes( len ); + } + + /** + * Returns the byte-encoded representation of this object of size max_byte_length(). + * @return The byte-encoded representation of this object. + */ + bytes to_bytes() const + { + auto len = max_byte_length(); + return to_bytes( len ); + } + /** * Returns the inverse of this element, i.e. 1 / this. * @return Inverse of this element. diff --git a/include/ack/fp.hpp b/include/ack/fp.hpp index 240a597..2d878cd 100644 --- a/include/ack/fp.hpp +++ b/include/ack/fp.hpp @@ -449,6 +449,7 @@ namespace ack { public: using base_type = field_element, IntT, PrimeFieldTag>; using base_type::base_type; + using base_type::to_bytes; /** * Constructs a zero finite field element. @@ -527,6 +528,18 @@ namespace ack { constexpr fp_element& operator=(const fp_element& other) = default; constexpr fp_element& operator=(fp_element&& other) = default; + /** + * Returns the max element bytes size. + * @return max element size. + */ + constexpr std::size_t max_byte_length() const + { + if ( !is_valid()) { + return 0; + } + return ( *pm_ - 1 ).byte_length(); + } + /** * Assigns big integer value to the finite field element. * If element is invalid, then it will not be changed. @@ -638,6 +651,27 @@ namespace ack { return v_.is_negative(); } + /** + * Returns the byte-encoded representation of this object. + * + * @param len - The desired length of the byte-encoded representation + * The full byte-encoded representation is returned if len is smaller + * than the byte length of the original object. + * + * @return The byte-encoded representation of this object. + */ + bytes to_bytes(std::size_t len) const + { + bytes ev; + if ( is_valid() ) { + ev = v_.to_bytes(); + if ( len > ev.size() ) { + ev = bytes( len - ev.size(), 0 ) + ev; + } + } + return ev; + } + /** * Calculates modular inverse of this finite field element. * @return (1 / this) % modulus. diff --git a/include/ack/types.hpp b/include/ack/types.hpp index b65809f..40f67ab 100644 --- a/include/ack/types.hpp +++ b/include/ack/types.hpp @@ -51,4 +51,21 @@ namespace ack { ds.skip( s ); return ds; } + + template, typename AllocB = std::allocator> + inline std::vector operator + (const std::vector& a, const std::vector& b) + { + std::vector c; + c.reserve(a.size() + b.size() ); + std::copy( a.begin(), a.end(), std::back_inserter( c) ); + std::copy( b.begin(), b.end(), std::back_inserter( c) ); + return c; + } + + template, typename AllocB = std::allocator> + inline std::vector& operator += (std::vector& a, const std::vector& b) + { + a.insert( a.end(), b.begin(), b.end() ); + return a; + } } \ No newline at end of file diff --git a/tests/include/ack/tests/fp_test.hpp b/tests/include/ack/tests/fp_test.hpp index 2dfa9b3..9d47e64 100644 --- a/tests/include/ack/tests/fp_test.hpp +++ b/tests/include/ack/tests/fp_test.hpp @@ -3552,6 +3552,117 @@ namespace ack::tests { fe /= b; REQUIRE_EQUAL( fe, "2F1F1FE098ED612AC09FE67F0DF9AE18AC979376C" ) } + + // misc + { + // encoding to bytes + auto mod = bn_t( "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" ); + auto fe = fpe_t( 0U, mod ); + REQUIRE_EQUAL( fe.to_bytes( 0 ), "00"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ), "00"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ), "0000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ), "000000"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "0000000000000000000000000000000000000000000000000000000000000000"_hex ) + + fe = 1; + REQUIRE_EQUAL( fe.to_bytes( 0 ), "01"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ), "01"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ), "0001"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ), "000001"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "0000000000000000000000000000000000000000000000000000000000000001"_hex ) + + fe = 0x0f; + REQUIRE_EQUAL( fe.to_bytes( 0 ), "0F"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ), "0F"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ), "000F"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ), "00000F"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "000000000000000000000000000000000000000000000000000000000000000F"_hex ) + + fe = 0xff; + REQUIRE_EQUAL( fe.to_bytes( 0 ), "FF"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ), "FF"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ), "00FF"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ), "0000FF"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "00000000000000000000000000000000000000000000000000000000000000FF"_hex ) + + fe = 0x100; + REQUIRE_EQUAL( fe.to_bytes( 0 ), "0100"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ), "0100"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ), "0100"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ), "000100"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "0000000000000000000000000000000000000000000000000000000000000100"_hex ) + + fe = 0x1000; + REQUIRE_EQUAL( fe.to_bytes( 0 ), "1000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ), "1000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ), "1000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ), "001000"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "0000000000000000000000000000000000000000000000000000000000001000"_hex ) + + fe = 0x10000; + REQUIRE_EQUAL( fe.to_bytes( 0 ), "010000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ), "010000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ), "010000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ), "010000"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "0000000000000000000000000000000000000000000000000000000000010000"_hex ) + + fe = 0x100000; + REQUIRE_EQUAL( fe.to_bytes( 0 ), "100000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ), "100000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ), "100000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ), "100000"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "0000000000000000000000000000000000000000000000000000000000100000"_hex ) + + fe = 0xffffff; + REQUIRE_EQUAL( fe.to_bytes( 0 ), "FFFFFF"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ), "FFFFFF"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ), "FFFFFF"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ), "FFFFFF"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "0000000000000000000000000000000000000000000000000000000000FFFFFF"_hex ) + + fe = 0x1000000; + REQUIRE_EQUAL( fe.to_bytes( 0 ), "01000000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ), "01000000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ), "01000000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ), "01000000"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "0000000000000000000000000000000000000000000000000000000001000000"_hex ) + + fe = 0x10000000; + REQUIRE_EQUAL( fe.to_bytes( 0 ), "10000000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ), "10000000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ), "10000000"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ), "10000000"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "0000000000000000000000000000000000000000000000000000000010000000"_hex ) + + fe = "2F1F1FE098ED612AC09FE67F0DF9AE18AC979376C"; + REQUIRE_EQUAL( fe.to_bytes( 0 ) , "02F1F1FE098ED612AC09FE67F0DF9AE18AC979376C"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ) , "02F1F1FE098ED612AC09FE67F0DF9AE18AC979376C"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ) , "02F1F1FE098ED612AC09FE67F0DF9AE18AC979376C"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ) , "02F1F1FE098ED612AC09FE67F0DF9AE18AC979376C"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 21 ), "02F1F1FE098ED612AC09FE67F0DF9AE18AC979376C"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 22 ), "0002F1F1FE098ED612AC09FE67F0DF9AE18AC979376C"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 23 ), "000002F1F1FE098ED612AC09FE67F0DF9AE18AC979376C"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "000000000000000000000002F1F1FE098ED612AC09FE67F0DF9AE18AC979376C"_hex ) + + fe = mod - 1; + REQUIRE_EQUAL( fe.to_bytes( 0 ) , "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 1 ) , "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 2 ) , "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 3 ) , "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 21 ), "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 22 ), "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 23 ), "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 32 ), "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 33 ), "00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"_hex ) + REQUIRE_EQUAL( fe.to_bytes( 34 ), "0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"_hex ) + REQUIRE_EQUAL( fe.to_bytes() , "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E"_hex ) + + // Should return empty bytes when invalid field element + fe = mod + 1; + REQUIRE_EQUAL( fe.to_bytes(), ""_hex ) + REQUIRE_EQUAL( fpe_t::zero().to_bytes(), ""_hex ) + REQUIRE_EQUAL( fpe_t::one().to_bytes(), ""_hex ) + } EOSIO_TEST_END // fp_element_test EOSIO_TEST_BEGIN(fp_test)