diff --git a/core/network/helpers/scale_message_read_writer.hpp b/core/network/helpers/scale_message_read_writer.hpp index 472a1eac69..de4e91d669 100644 --- a/core/network/helpers/scale_message_read_writer.hpp +++ b/core/network/helpers/scale_message_read_writer.hpp @@ -43,7 +43,7 @@ namespace kagome::network { } if (read_res.value()) { - auto msg_res = scale::decode(*read_res.value()); + auto msg_res = ::scale::decode(*read_res.value()); if (!msg_res) { return cb(msg_res.error()); } diff --git a/core/scale/encoder/primitives.hpp b/core/scale/encoder/primitives.hpp new file mode 100644 index 0000000000..bc3654242c --- /dev/null +++ b/core/scale/encoder/primitives.hpp @@ -0,0 +1,476 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef KAGOME_SCALE_ENCODER_PRIMITIVES_HPP +#define KAGOME_SCALE_ENCODER_PRIMITIVES_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils/struct_to_tuple.hpp" + +namespace kagome::scale { + + template + constexpr void putByte(const F &func, const uint8_t *const val, size_t count); + + template + constexpr void encode(const F &func, const std::tuple &v); + + template + constexpr void encode(const F &func, const T &t, const Args &...args); + + template + constexpr void encode(const F &func, const std::vector &c); + + template + constexpr void encode(const FN &func, const std::pair &p); + + template + constexpr void encode(const F &func, const gsl::span &c); + + template + constexpr void encode(const F &func, const std::array &c); + + template + constexpr void encode(const F &func, const T (&c)[N]); + + template + constexpr void encode(const F &func, const std::map &c); + + template + constexpr void encode(const F &func, const std::shared_ptr &v); + + template + constexpr void encode(const F &func, const std::string_view &v); + + template + constexpr void encode(const F &func, const std::string &v); + + template + constexpr void encode(const F &func, const std::unique_ptr &v); + + template + constexpr void encode(const F &func, const std::list &c); + + template + constexpr void encode(const F &func, const std::deque &c); + + template + void encode(const F &func, const boost::variant &v); + + template + void encode(const F &func, const boost::variant &v); + + template + void encode(const F &func, const ::scale::CompactInteger &value); + + template + void encode(const F &func, const ::scale::BitVec &value); + + template + void encode(const F &func, const std::optional &value); + + template + void encode(const F &func, const std::optional &value); + + template >, bool> = true> + constexpr void encode(const F &func, const T &v) { + using I = std::decay_t; + if constexpr (std::is_integral_v) { + if constexpr (std::is_same_v) { + const uint8_t byte = (v ? 1u : 0u); + putByte(func, &byte, 1ul); + return; + } + + if constexpr (sizeof(I) == 1u) { + putByte(func, (const uint8_t *)&v, size_t(1ull)); + return; + } + + constexpr size_t size = sizeof(I); +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + if constexpr (size == 8) { + v = __builtin_bswap64(v); + } else if constexpr (size == 4) { + v = __builtin_bswap32(v); + } else if constexpr (size == 2) { + v = __builtin_bswap16(v); + } else { + UNREACHABLE; + } +#endif + + putByte(func, (uint8_t *)&v, size); + } else { + encode(func, utils::to_tuple_refs(v)); + } + } + + template >, bool> = true> + constexpr void encode(const F &func, const T &value) { + encode(func, static_cast>>(value)); + } + + template + constexpr void encode(const F &func, const T &t, const Args &...args) { + encode(func, t); + encode(func, args...); + } + + template + outcome::result> encode(const Args &...args) { + std::vector res; + encode( + [&](const uint8_t *const val, size_t count) { + if (count != 0ull) { + res.insert(res.end(), &val[0], &val[count]); + } + }, + args...); + return res; + } + + inline size_t countBytes(::scale::CompactInteger x) { + namespace mp = boost::multiprecision; + const size_t size = x.backend().size(); + const mp::limb_type *const p = x.backend().limbs(); + + auto counter = (size - 1) * sizeof(mp::limb_type); + auto value = p[size - 1]; + do { + ++counter; + value >>= 8; + } while (value); + return counter; + } + + template + constexpr void putByte(const F &func, + const uint8_t *const val, + size_t count) { + func(val, count); + } + + template + void encode(const F &func, const boost::variant &v) { + using T = std::tuple_element_t>; + if (v.which() == I) { + encode(func, I); + encode(func, boost::get(v)); + return; + } + if constexpr (sizeof...(Ts) > I + 1) { + encode(func, v); + } + } + + template + constexpr void encode(const F &func, const T (&c)[N]) { + using E = std::decay_t; + if constexpr (std::is_integral_v && sizeof(E) == 1u) { + putByte(func, c, N); + } else { + for (const auto &e : c) { + encode(func, e); + } + } + } + + template + void encode(const F &func, const boost::variant &v) { + encode(func, v); + } + + template , + std::enable_if_t, bool> = true> + constexpr void encodeCompactSmall(const F &func, T val) { + constexpr size_t sz = sizeof(I); + constexpr uint8_t mask = 3 << 6; + const uint8_t msb_byte = (uint8_t)(val >> ((sz - 1ull) * 8ull)); + + BOOST_ASSERT_MSG((msb_byte & mask) == 0, + "Unexpected compact value in encoder"); + val <<= 2; + val += (sz / 2ull); + + encode(func, val); + } + + template + void encodeCompact(const F &func, uint64_t val) { + if (val < ::scale::compact::EncodingCategoryLimits::kMinUint16) { + encodeCompactSmall(func, static_cast(val)); + return; + } + + if (val < ::scale::compact::EncodingCategoryLimits::kMinUint32) { + encodeCompactSmall(func, static_cast(val)); + return; + } + + if (val < ::scale::compact::EncodingCategoryLimits::kMinBigInteger) { + encodeCompactSmall(func, static_cast(val)); + return; + } + + if (val >= (1ul << 63ull)) { + encode(func, ::scale::CompactInteger{val}); + return; + } + + static_assert(0x8000000000000000 == (1ull << 63ull), "11111"); + const size_t bigIntLength = sizeof(uint64_t) - (__builtin_clzll(val) / 8); + + uint8_t result[sizeof(uint64_t) + sizeof(uint8_t)]; + result[0] = (bigIntLength - 4) * 4 + 3; // header + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + *(uint64_t *)&result[1] = __builtin_bswap64(val); +#else + *(uint64_t *)&result[1] = val; +#endif + + putByte(func, result, bigIntLength + 1ull); + } + + template + constexpr void encode(const F &func, const std::string &v) { + encode(func, std::string_view{v}); + } + + template + constexpr void encode(const F &func, const std::string_view &v) { + encodeCompact(func, v.size()); + putByte(func, (const uint8_t *)v.data(), v.size()); + } + + template + void encode(const F &func, const ::scale::BitVec &v) { + const size_t bitsCount = v.bits.size(); + const size_t bytesCount = ((bitsCount + 7ull) >> 3ull); + const size_t blocksCount = ((bytesCount + 7ull) >> 3ull); + + encodeCompact(func, bitsCount); + uint64_t result; + size_t bitCounter = 0ull; + for (size_t ix = 0ull; ix < blocksCount; ++ix) { + result = 0ull; + size_t remains = std::min(size_t(64ull), bitsCount - bitCounter); + do { + result |= ((v.bits[bitCounter] ? 1ull : 0ull) << (bitCounter % 64ull)); + ++bitCounter; + } while (--remains); + + const size_t bits = (bitCounter % 64ull); + const size_t bytes = + (bits != 0ull) ? ((bits + 7ull) >> 3ull) : sizeof(result); + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + if constexpr (size == 8) { + v = __builtin_bswap64(v); + } else if constexpr (size == 4) { + v = __builtin_bswap32(v); + } else if constexpr (size == 2) { + v = __builtin_bswap16(v); + } else { + UNREACHABLE; + } +#endif + putByte(func, (uint8_t *)&result, bytes); + } + } + + template + void encode(const F &func, const ::scale::CompactInteger &value) { + if (value < 0) { + raise(::scale::EncodeError::NEGATIVE_COMPACT_INTEGER); + } + + if (value < ::scale::compact::EncodingCategoryLimits::kMinUint16) { + encodeCompactSmall(func, value.convert_to()); + return; + } + + if (value < ::scale::compact::EncodingCategoryLimits::kMinUint32) { + encodeCompactSmall(func, value.convert_to()); + return; + } + + if (value < ::scale::compact::EncodingCategoryLimits::kMinBigInteger) { + encodeCompactSmall(func, value.convert_to()); + return; + } + + constexpr size_t kReserved = 68ull; + const size_t bigIntLength = countBytes(value); + + if (bigIntLength >= kReserved) { + raise(::scale::EncodeError::COMPACT_INTEGER_TOO_BIG); + } + + uint8_t result[kReserved]; + result[0] = (bigIntLength - 4) * 4 + 3; // header + + namespace mp = boost::multiprecision; + const size_t size = value.backend().size(); + const mp::limb_type *const p = value.backend().limbs(); + + constexpr size_t limb_sz = sizeof(mp::limb_type); + size_t ix = 0ull; + for (; ix < size; ++ix) { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + if constexpr (limb_sz == 8) { + *(mp::limb_type *)&result[1ull + ix * limb_sz] = + __builtin_bswap64(p[ix]); + } else if constexpr (limb_sz == 4) { + *(mp::limb_type *)&result[1ull + ix * limb_sz] = + __builtin_bswap32(p[ix]); + } else if constexpr (limb_sz == 2) { + *(mp::limb_type *)&result[1ull + ix * limb_sz] = + __builtin_bswap16(p[ix]); + } else { + UNREACHABLE; + } +#else + *(mp::limb_type *)&result[1ull + ix * limb_sz] = p[ix]; +#endif + } + + putByte(func, result, bigIntLength + 1ull); + } + + template < + typename F, + typename It, + typename = std::enable_if_t< + !std::is_same_v::value_type, void>>> + constexpr void encode(const F &func, It begin, It end) { + while (begin != end) { + encode(func, *begin); + ++begin; + } + } + + template + constexpr void encode(const F &func, const gsl::span &c) { + if constexpr (S == -1) { + encodeCompact(func, c.size()); + encode(func, c.begin(), c.end()); + } else { + using E = std::decay_t; + if constexpr (std::is_integral_v && sizeof(E) == 1u) { + putByte(func, c.data(), c.size()); + } else { + for (const auto &e : c) { + encode(func, e); + } + } + } + } + + template + constexpr void encode(const F &func, const std::array &c) { + for (const auto &e : c) { + encode(func, e); + } + } + + template + constexpr void encode(const F &func, const std::map &c) { + encodeCompact(func, c.size()); + encode(func, c.begin(), c.end()); + } + + template + constexpr void encode(const FN &func, const std::pair &p) { + encode(func, p.first); + encode(func, p.second); + } + + template + constexpr void encode(const F &func, const std::vector &c) { + encodeCompact(func, c.size()); + encode(func, c.begin(), c.end()); + } + + template + constexpr void encode(const F &func, const std::shared_ptr &v) { + if (v == nullptr) { + raise(::scale::EncodeError::DEREF_NULLPOINTER); + } + encode(func, *v); + } + + template + constexpr void encode(const F &func, const std::unique_ptr &v) { + if (v == nullptr) { + raise(::scale::EncodeError::DEREF_NULLPOINTER); + } + encode(func, *v); + } + + template + constexpr void encode(const F &func, const std::list &c) { + encodeCompact(func, c.size()); + encode(func, c.begin(), c.end()); + } + + template + constexpr void encode(const F &func, const std::deque &c) { + encodeCompact(func, c.size()); + encode(func, c.begin(), c.end()); + } + + template + constexpr void encode(const F &func, const std::tuple &v) { + if constexpr (sizeof...(Ts) > 0) { + std::apply([&](const auto &...s) { (..., encode(func, s)); }, v); + } + } + + template + void encode(const F &func, const std::optional &v) { + enum class OptionalBool : uint8_t { + NONE = 0u, + OPT_TRUE = 1u, + OPT_FALSE = 2u + }; + + auto result = OptionalBool::OPT_TRUE; + if (!v.has_value()) { + result = OptionalBool::NONE; + } else if (!*v) { + result = OptionalBool::OPT_FALSE; + } + encode(func, result); + } + + template + void encode(const F &func, const std::optional &v) { + if (!v.has_value()) { + encode(func, uint8_t(0u)); + } else { + encode(func, uint8_t(1u)); + encode(func, *v); + } + } + +} // namespace kagome::scale + +#endif // KAGOME_SCALE_ENCODER_PRIMITIVES_HPP diff --git a/core/scale/kagome_scale.hpp b/core/scale/kagome_scale.hpp new file mode 100644 index 0000000000..2502bcf560 --- /dev/null +++ b/core/scale/kagome_scale.hpp @@ -0,0 +1,135 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef KAGOME_KAGOME_SCALE_HPP +#define KAGOME_KAGOME_SCALE_HPP + +#include +#include "common/blob.hpp" +#include "consensus/babe/types/babe_block_header.hpp" +#include "consensus/babe/types/seal.hpp" +#include "primitives/block_header.hpp" +#include "primitives/block_id.hpp" +#include "primitives/justification.hpp" +#include "scale/encode_append.hpp" + +namespace kagome::scale { + using CompactInteger = ::scale::CompactInteger; + + template + constexpr void encode(const F &func, const primitives::BlockHeader &bh); + + template + constexpr void encode( + const F &func, const common::SLVector &c); + + template + constexpr void encode(const F &func, const Tagged &c); + + template + constexpr void encode(const F &func, const common::SLBuffer &c); + + template + constexpr void encode(const F &func, const primitives::Other &c); + + template + constexpr void encode(const F &func, const primitives::Consensus &c); + + template + constexpr void encode(const F &func, const primitives::Seal &c); + + template + constexpr void encode(const F &func, const primitives::PreRuntime &c); + + template + constexpr void encode(const F &func, + const primitives::RuntimeEnvironmentUpdated &c); + + template + constexpr void encode(const F &func, const ::scale::EncodeOpaqueValue &c); + + template + constexpr void encode(const F &func, + const consensus::babe::BabeBlockHeader &bh); + +} // namespace kagome::scale + +#include "scale/encoder/primitives.hpp" + +namespace kagome::scale { + + template + constexpr void encode(const F &func, const primitives::BlockHeader &bh) { + encode(func, bh.parent_hash); + encodeCompact(func, bh.number); + encode(func, bh.state_root); + encode(func, bh.extrinsics_root); + encode(func, bh.digest); + } + + template + constexpr void encode(const F &func, + const consensus::babe::BabeBlockHeader &bh) { + encode(func, bh.slot_assignment_type); + encode(func, bh.authority_index); + encode(func, bh.slot_number); + + if (bh.needVRFCheck()) { + encode(func, bh.vrf_output); + } + } + + template + constexpr void encode( + const F &func, const common::SLVector &c) { + encode(func, static_cast &>(c)); + } + + template + constexpr void encode(const F &func, const Tagged &c) { + if constexpr (std::is_scalar_v) { + encode(func, c.Wrapper::value); + } else { + encode(func, static_cast(c)); + } + } + + template + constexpr void encode(const F &func, const common::SLBuffer &c) { + encode(func, static_cast &>(c)); + } + + template + constexpr void encode(const F &func, const primitives::Other &c) { + encode(func, static_cast(c)); + } + + template + constexpr void encode(const F &func, const primitives::Consensus &c) { + encode(func, static_cast(c)); + } + + template + constexpr void encode(const F &func, const primitives::Seal &c) { + encode(func, static_cast(c)); + } + + template + constexpr void encode(const F &func, const primitives::PreRuntime &c) { + encode(func, static_cast(c)); + } + + template + constexpr void encode(const F &func, + const primitives::RuntimeEnvironmentUpdated &c) {} + + template + constexpr void encode(const F &func, const ::scale::EncodeOpaqueValue &c) { + putByte(func, c.v.data(), c.v.size()); + } + +} // namespace kagome::scale + +#endif // KAGOME_KAGOME_SCALE_HPP diff --git a/core/utils/struct_to_tuple.hpp b/core/utils/struct_to_tuple.hpp new file mode 100644 index 0000000000..51df18ed49 --- /dev/null +++ b/core/utils/struct_to_tuple.hpp @@ -0,0 +1,145 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef KAGOME_STRUCT_TO_TUPLE_HPP +#define KAGOME_STRUCT_TO_TUPLE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#define REP0(X) +#define REP1(X) X +#define REP2(X) REP1(X), X +#define REP3(X) REP2(X), X +#define REP4(X) REP3(X), X +#define REP5(X) REP4(X), X +#define REP6(X) REP5(X), X +#define REP7(X) REP6(X), X +#define REP8(X) REP7(X), X +#define REP9(X) REP8(X), X +#define REP10(X) REP9(X), X + +#define REP0Y(X) +#define REP1Y(X) X##1 +#define REP2Y(X) REP1Y(X), X##2 +#define REP3Y(X) REP2Y(X), X##3 +#define REP4Y(X) REP3Y(X), X##4 +#define REP5Y(X) REP4Y(X), X##5 +#define REP6Y(X) REP5Y(X), X##6 +#define REP7Y(X) REP6Y(X), X##7 +#define REP8Y(X) REP7Y(X), X##8 +#define REP9Y(X) REP8Y(X), X##9 + +#define REP0Y_REF(X) +#define REP1Y_REF(X) std::cref(X##1) +#define REP2Y_REF(X) REP1Y_REF(X), std::cref(X##2) +#define REP3Y_REF(X) REP2Y_REF(X), std::cref(X##3) +#define REP4Y_REF(X) REP3Y_REF(X), std::cref(X##4) +#define REP5Y_REF(X) REP4Y_REF(X), std::cref(X##5) +#define REP6Y_REF(X) REP5Y_REF(X), std::cref(X##6) +#define REP7Y_REF(X) REP6Y_REF(X), std::cref(X##7) +#define REP8Y_REF(X) REP7Y_REF(X), std::cref(X##8) +#define REP9Y_REF(X) REP8Y_REF(X), std::cref(X##9) + +#define REPEAT(TENS, ONES, X) REP##TENS(REP10(X)) REP##ONES(X) +#define REPEATY(ONES, X) REP##ONES##Y(X) +#define REPEATY_REF(ONES, X) REP##ONES##Y_REF(X) + +#define TO_TUPLE_N(ONES) \ + if constexpr (is_braces_constructible{}) { \ + const auto &[REPEATY(ONES, p)] = object; \ + return std::make_tuple(REPEATY_REF(ONES, p)); \ + } + +#define TO_TUPLE1 \ + TO_TUPLE_N(1) else { \ + return std::make_tuple(); \ + } +#define TO_TUPLE2 TO_TUPLE_N(2) else TO_TUPLE1 +#define TO_TUPLE3 TO_TUPLE_N(3) else TO_TUPLE2 +#define TO_TUPLE4 TO_TUPLE_N(4) else TO_TUPLE3 +#define TO_TUPLE5 TO_TUPLE_N(5) else TO_TUPLE4 +#define TO_TUPLE6 TO_TUPLE_N(6) else TO_TUPLE5 +#define TO_TUPLE7 TO_TUPLE_N(7) else TO_TUPLE6 +#define TO_TUPLE8 TO_TUPLE_N(8) else TO_TUPLE7 +#define TO_TUPLE9 TO_TUPLE_N(9) else TO_TUPLE8 + +namespace kagome::utils { + + template + decltype(void(T{{std::declval()}...}), std::true_type{}) + test_is_braces_constructible(int); + + template + std::false_type test_is_braces_constructible(...); + + template + using is_braces_constructible = + decltype(test_is_braces_constructible(0)); + + struct any_type { + template + constexpr operator T(); // non explicit + }; + + template + inline auto to_tuple_refs(const T &object) noexcept { + using type = std::decay_t; + TO_TUPLE9; + } +} // namespace kagome::utils + +#undef REP0 +#undef REP1 +#undef REP2 +#undef REP3 +#undef REP4 +#undef REP5 +#undef REP6 +#undef REP7 +#undef REP8 +#undef REP9 +#undef REP10 +#undef REP0Y +#undef REP1Y +#undef REP2Y +#undef REP3Y +#undef REP4Y +#undef REP5Y +#undef REP6Y +#undef REP7Y +#undef REP8Y +#undef REP9Y +#undef REP0Y_REF +#undef REP1Y_REF +#undef REP2Y_REF +#undef REP3Y_REF +#undef REP4Y_REF +#undef REP5Y_REF +#undef REP6Y_REF +#undef REP7Y_REF +#undef REP8Y_REF +#undef REP9Y_REF +#undef REPEAT +#undef REPEATY +#undef REPEATY_REF +#undef TO_TUPLE_N +#undef TO_TUPLE1 +#undef TO_TUPLE2 +#undef TO_TUPLE3 +#undef TO_TUPLE4 +#undef TO_TUPLE5 +#undef TO_TUPLE6 +#undef TO_TUPLE7 +#undef TO_TUPLE8 +#undef TO_TUPLE9 + +#endif // KAGOME_STRUCT_TO_TUPLE_HPP diff --git a/test/core/api/service/system/system_api_test.cpp b/test/core/api/service/system/system_api_test.cpp index f7c2aaf0bf..b306f394c1 100644 --- a/test/core/api/service/system/system_api_test.cpp +++ b/test/core/api/service/system/system_api_test.cpp @@ -14,9 +14,11 @@ #include "mock/core/network/peer_manager_mock.hpp" #include "mock/core/runtime/account_nonce_api_mock.hpp" #include "mock/core/transaction_pool/transaction_pool_mock.hpp" +#include "scale/kagome_scale.hpp" #include "scale/scale.hpp" #include "testutil/literals.hpp" #include "testutil/outcome.hpp" +#include "testutil/scale_test_comparator.hpp" using kagome::api::SystemApi; using kagome::api::SystemApiImpl; @@ -122,7 +124,9 @@ TEST_F(SystemApiTest, GetNonceWithPendingTxs) { std::vector>> ready_txs; for (size_t i = 0; i < kReadyTxNum; i++) { - EXPECT_OUTCOME_TRUE(enc_nonce, scale::encode(kAccountId, kInitialNonce + i)) + EXPECT_OUTCOME_TRUE( + enc_nonce, + testutil::scaleEncodeAndCompareWithRef(kAccountId, kInitialNonce + i)) encoded_nonces[i] = std::move(enc_nonce); ready_txs.emplace_back( std::make_pair(Hash256{{static_cast(i)}}, diff --git a/test/core/blockchain/block_header_repository_test.cpp b/test/core/blockchain/block_header_repository_test.cpp index 5da83a33c4..c97a9633ea 100644 --- a/test/core/blockchain/block_header_repository_test.cpp +++ b/test/core/blockchain/block_header_repository_test.cpp @@ -13,10 +13,12 @@ #include "blockchain/impl/block_header_repository_impl.hpp" #include "blockchain/impl/storage_util.hpp" #include "crypto/hasher/hasher_impl.hpp" +#include "scale/kagome_scale.hpp" #include "scale/scale.hpp" #include "testutil/literals.hpp" #include "testutil/outcome.hpp" #include "testutil/prepare_loggers.hpp" +#include "testutil/scale_test_comparator.hpp" #include "testutil/storage/base_rocksdb_test.hpp" using kagome::blockchain::BlockHeaderRepository; @@ -48,7 +50,10 @@ class BlockHeaderRepository_Test : public test::BaseRocksDB_Test { outcome::result storeHeader(BlockNumber num, BlockHeader h) { BlockHeader header = std::move(h); header.number = num; - OUTCOME_TRY(enc_header, scale::encode(header)); + + [[maybe_unused]] auto __1 = testutil::scaleEncodeAndCompareWithRef( + ::scale::CompactInteger(4294967295)); + OUTCOME_TRY(enc_header, testutil::scaleEncodeAndCompareWithRef(header)); auto hash = hasher_->blake2b_256(enc_header); OUTCOME_TRY(putToSpace(*rocks_, Space::kHeader, hash, Buffer{enc_header})); @@ -92,7 +97,8 @@ TEST_F(BlockHeaderRepository_Test, UnexistingHeader) { } BlockHeader not_in_storage = getDefaultHeader(); not_in_storage.number = chosen_number; - EXPECT_OUTCOME_TRUE(enc_header, scale::encode(not_in_storage)) + EXPECT_OUTCOME_TRUE(enc_header, + testutil::scaleEncodeAndCompareWithRef(not_in_storage)) auto hash = hasher_->blake2b_256(enc_header); EXPECT_OUTCOME_FALSE_1(header_repo_->getBlockHeader(hash)) EXPECT_OUTCOME_FALSE_1(header_repo_->getHashById(chosen_number)) @@ -146,6 +152,22 @@ TEST_P(BlockHeaderRepository_NumberParametrized_Test, GetHeader) { ASSERT_EQ(header_by_hash, header_should_be); } +TEST_P(BlockHeaderRepository_NumberParametrized_Test, bitvec) { + auto create_bit_vec = [](size_t count) { + ::scale::BitVec bv; + for (size_t i = 0; i < count; ++i) { + bv.bits.push_back((i % 2ull) == 0ull); + } + + return bv; + }; + + for (size_t i = 0ull; i < 200ull; ++i) { + [[maybe_unused]] auto __1 = + testutil::scaleEncodeAndCompareWithRef(create_bit_vec(i)); + } +} + INSTANTIATE_TEST_SUITE_P(Numbers, BlockHeaderRepository_NumberParametrized_Test, testing::ValuesIn(ParamValues)); diff --git a/test/core/blockchain/block_storage_test.cpp b/test/core/blockchain/block_storage_test.cpp index 09d8291a43..d7ee3d2d91 100644 --- a/test/core/blockchain/block_storage_test.cpp +++ b/test/core/blockchain/block_storage_test.cpp @@ -11,6 +11,7 @@ #include "mock/core/crypto/hasher_mock.hpp" #include "mock/core/storage/persistent_map_mock.hpp" #include "mock/core/storage/spaced_storage_mock.hpp" +#include "scale/kagome_scale.hpp" #include "scale/scale.hpp" #include "storage/database_error.hpp" #include "testutil/outcome.hpp" diff --git a/test/core/blockchain/block_tree_test.cpp b/test/core/blockchain/block_tree_test.cpp index d91050d15d..ea0f05ac28 100644 --- a/test/core/blockchain/block_tree_test.cpp +++ b/test/core/blockchain/block_tree_test.cpp @@ -33,6 +33,9 @@ #include "testutil/outcome.hpp" #include "testutil/outcome/dummy_error.hpp" #include "testutil/prepare_loggers.hpp" +#include "testutil/scale_test_comparator.hpp" + +#include "scale/kagome_scale.hpp" using namespace kagome; using namespace storage; @@ -176,7 +179,7 @@ struct BlockTreeTest : public testing::Test { * @return block, which was added, along with its hash */ BlockHash addBlock(const Block &block) { - auto encoded_block = scale::encode(block).value(); + auto encoded_block = testutil::scaleEncodeAndCompareWithRef(block).value(); auto hash = hasher_->blake2b_256(encoded_block); primitives::BlockInfo block_info(block.header.number, hash); @@ -276,12 +279,16 @@ struct BlockTreeTest : public testing::Test { .authority_index = 0, .slot_number = slot, }; - common::Buffer encoded_header{scale::encode(babe_header).value()}; + + auto m = testutil::scaleEncodeAndCompareWithRef(babe_header).value(); + common::Buffer encoded_header{m}; digest.emplace_back( primitives::PreRuntime{{primitives::kBabeEngineId, encoded_header}}); kagome::consensus::babe::Seal seal{}; - common::Buffer encoded_seal{scale::encode(seal).value()}; + auto m1 = testutil::scaleEncodeAndCompareWithRef(seal).value(); + + common::Buffer encoded_seal{m1}; digest.emplace_back( primitives::Seal{{primitives::kBabeEngineId, encoded_seal}}); @@ -401,7 +408,9 @@ TEST_F(BlockTreeTest, Finalize) { auto hash = addBlock(new_block); Justification justification{{0x45, 0xF4}}; - auto encoded_justification = scale::encode(justification).value(); + auto encoded_justification = + testutil::scaleEncodeAndCompareWithRef(justification).value(); + EXPECT_CALL(*storage_, getJustification(kFinalizedBlockInfo.hash)) .WillRepeatedly(Return(outcome::success(justification))); EXPECT_CALL(*storage_, getJustification(hash)) @@ -464,7 +473,9 @@ TEST_F(BlockTreeTest, FinalizeWithPruning) { auto C1_hash = addBlock(C1_block); Justification justification{{0x45, 0xF4}}; - auto encoded_justification = scale::encode(justification).value(); + auto encoded_justification = + testutil::scaleEncodeAndCompareWithRef(justification).value(); + EXPECT_CALL(*storage_, getJustification(B1_hash)) .WillRepeatedly(Return(outcome::failure(boost::system::error_code{}))); EXPECT_CALL(*storage_, putJustification(justification, B1_hash)) @@ -532,7 +543,9 @@ TEST_F(BlockTreeTest, FinalizeWithPruningDeepestLeaf) { auto C1_hash = addBlock(C1_block); Justification justification{{0x45, 0xF4}}; - auto encoded_justification = scale::encode(justification).value(); + auto encoded_justification = + testutil::scaleEncodeAndCompareWithRef(justification).value(); + EXPECT_CALL(*storage_, putJustification(justification, B_hash)) .WillRepeatedly(Return(outcome::success())); EXPECT_CALL(*storage_, getBlockHeader(B_hash)) diff --git a/test/core/consensus/babe/babe_test.cpp b/test/core/consensus/babe/babe_test.cpp index f05312263a..06876e4e57 100644 --- a/test/core/consensus/babe/babe_test.cpp +++ b/test/core/consensus/babe/babe_test.cpp @@ -36,10 +36,12 @@ #include "mock/core/storage/trie/trie_storage_mock.hpp" #include "mock/core/transaction_pool/transaction_pool_mock.hpp" #include "runtime/runtime_context.hpp" +#include "scale/kagome_scale.hpp" #include "storage/trie/serialization/ordered_trie_hash.hpp" #include "testutil/lazy.hpp" #include "testutil/literals.hpp" #include "testutil/prepare_loggers.hpp" +#include "testutil/scale_test_comparator.hpp" #include "testutil/sr25519_utils.hpp" using namespace kagome; @@ -77,12 +79,14 @@ static Digest make_digest(BabeSlotNumber slot) { .authority_index = 0, .slot_number = slot, }; - common::Buffer encoded_header{scale::encode(babe_header).value()}; + common::Buffer encoded_header{ + testutil::scaleEncodeAndCompareWithRef(babe_header).value()}; digest.emplace_back( primitives::PreRuntime{{primitives::kBabeEngineId, encoded_header}}); consensus::babe::Seal seal{}; - common::Buffer encoded_seal{scale::encode(seal).value()}; + common::Buffer encoded_seal{ + testutil::scaleEncodeAndCompareWithRef(seal).value()}; digest.emplace_back( primitives::Seal{{primitives::kBabeEngineId, encoded_seal}}); @@ -209,8 +213,8 @@ class BabeTest : public testing::Test { epoch_.epoch_number = 0; // add extrinsics root to the header - std::vector encoded_exts( - {common::Buffer(scale::encode(extrinsic_).value())}); + std::vector encoded_exts({common::Buffer( + testutil::scaleEncodeAndCompareWithRef(extrinsic_).value())}); created_block_.header.extrinsics_root = common::Hash256::fromSpan( kagome::storage::trie::calculateOrderedTrieHash( diff --git a/test/core/consensus/validation/block_validator_test.cpp b/test/core/consensus/validation/block_validator_test.cpp index 529510919f..f58cd00078 100644 --- a/test/core/consensus/validation/block_validator_test.cpp +++ b/test/core/consensus/validation/block_validator_test.cpp @@ -19,6 +19,7 @@ #include "testutil/outcome.hpp" #include "testutil/prepare_loggers.hpp" #include "testutil/primitives/mp_utils.hpp" +#include "testutil/scale_test_comparator.hpp" using namespace kagome; using namespace blockchain; @@ -49,6 +50,8 @@ using testing::ReturnRef; using testutil::createHash256; +#include "scale/kagome_scale.hpp" + namespace sr25519_constants = kagome::crypto::constants::sr25519; class BlockValidatorTest : public testing::Test { @@ -76,7 +79,7 @@ class BlockValidatorTest : public testing::Test { // seal the block Seal seal{sr25519_signature}; - common::Buffer encoded_seal{scale::encode(seal).value()}; + common::Buffer encoded_seal{::scale::encode(seal).value()}; block.header.digest.push_back( kagome::primitives::Seal{{kEngineId, encoded_seal}}); @@ -108,7 +111,7 @@ class BlockValidatorTest : public testing::Test { authority_index_, slot_number_, {vrf_value_, vrf_proof_}}; - Buffer encoded_babe_header_{scale::encode(babe_header_).value()}; + Buffer encoded_babe_header_{::scale::encode(babe_header_).value()}; BlockHeader block_header_{ .parent_hash = parent_hash_, @@ -121,11 +124,10 @@ class BlockValidatorTest : public testing::Test { primitives::AuthorityList authorities_; primitives::BabeConfiguration config_{ - .leadership_rate = {3,4}, + .leadership_rate = {3, 4}, .authorities = {}, .randomness = Randomness{uint256_to_le_bytes(475995757021)}, - .allowed_slots = {} - }; + .allowed_slots = {}}; }; /** @@ -138,7 +140,19 @@ TEST_F(BlockValidatorTest, Success) { // get an encoded pre-seal part of the block's header auto block_copy = valid_block_; block_copy.header.digest.pop_back(); - auto encoded_block_copy = scale::encode(block_copy.header).value(); + + common::Buffer b; + b.push_back(10); + testutil::scaleEncodeAndCompareWithRef(b); + + using TestDataT = boost::variant; + TestDataT d = primitives::Other{std::move(b)}; + [[maybe_unused]] auto __1 = testutil::scaleEncodeAndCompareWithRef(d); + [[maybe_unused]] auto __2 = + testutil::scaleEncodeAndCompareWithRef(block_copy.header.digest).value(); + + auto encoded_block_copy = + testutil::scaleEncodeAndCompareWithRef(block_copy.header).value(); Hash256 encoded_block_copy_hash{}; // not a real hash, but don't want to // actually take it std::copy(encoded_block_copy.begin(), @@ -190,7 +204,8 @@ TEST_F(BlockValidatorTest, LessDigestsThanNeeded) { TEST_F(BlockValidatorTest, NoBabeHeader) { auto block_copy = valid_block_; block_copy.header.digest.pop_back(); - auto encoded_block_copy = scale::encode(block_copy.header).value(); + auto encoded_block_copy = + testutil::scaleEncodeAndCompareWithRef(block_copy.header).value(); Hash256 encoded_block_copy_hash{}; // not a real hash, but don't want to // actually take it std::copy(encoded_block_copy.begin(), @@ -223,7 +238,9 @@ TEST_F(BlockValidatorTest, NoAuthority) { // GIVEN auto block_copy = valid_block_; block_copy.header.digest.pop_back(); - auto encoded_block_copy = scale::encode(block_copy.header).value(); + auto encoded_block_copy = + testutil::scaleEncodeAndCompareWithRef(block_copy.header).value(); + Hash256 encoded_block_copy_hash{}; std::copy(encoded_block_copy.begin(), encoded_block_copy.begin() + Hash256::size(), @@ -262,7 +279,8 @@ TEST_F(BlockValidatorTest, SignatureVerificationFail) { // GIVEN auto block_copy = valid_block_; block_copy.header.digest.pop_back(); - auto encoded_block_copy = scale::encode(block_copy.header).value(); + auto encoded_block_copy = + testutil::scaleEncodeAndCompareWithRef(block_copy.header).value(); Hash256 encoded_block_copy_hash{}; std::copy(encoded_block_copy.begin(), encoded_block_copy.begin() + Hash256::size(), @@ -302,7 +320,8 @@ TEST_F(BlockValidatorTest, VRFFail) { // GIVEN auto block_copy = valid_block_; block_copy.header.digest.pop_back(); - auto encoded_block_copy = scale::encode(block_copy.header).value(); + auto encoded_block_copy = + testutil::scaleEncodeAndCompareWithRef(block_copy.header).value(); Hash256 encoded_block_copy_hash{}; std::copy(encoded_block_copy.begin(), encoded_block_copy.begin() + Hash256::size(), @@ -341,7 +360,8 @@ TEST_F(BlockValidatorTest, ThresholdGreater) { // GIVEN auto block_copy = valid_block_; block_copy.header.digest.pop_back(); - auto encoded_block_copy = scale::encode(block_copy.header).value(); + auto encoded_block_copy = + testutil::scaleEncodeAndCompareWithRef(block_copy.header).value(); Hash256 encoded_block_copy_hash{}; std::copy(encoded_block_copy.begin(), encoded_block_copy.begin() + Hash256::size(), diff --git a/test/core/host_api/child_storage_extension_test.cpp b/test/core/host_api/child_storage_extension_test.cpp index 3f8ba568e8..4c3ec5a839 100644 --- a/test/core/host_api/child_storage_extension_test.cpp +++ b/test/core/host_api/child_storage_extension_test.cpp @@ -17,6 +17,7 @@ #include "mock/core/storage/trie/trie_batches_mock.hpp" #include "runtime/ptr_size.hpp" #include "scale/encode_append.hpp" +#include "scale/kagome_scale.hpp" #include "storage/predefined_keys.hpp" #include "storage/trie/polkadot_trie/trie_error.hpp" #include "storage/trie/types.hpp" @@ -24,6 +25,7 @@ #include "testutil/outcome.hpp" #include "testutil/outcome/dummy_error.hpp" #include "testutil/prepare_loggers.hpp" +#include "testutil/scale_test_comparator.hpp" using kagome::common::Buffer; using kagome::host_api::ChildStorageExtension; @@ -124,7 +126,8 @@ TEST_P(ReadOutcomeParameterizedTest, GetTest) { std::vector encoded_opt_value; if (GetParam()) { - encoded_opt_value = scale::encode(GetParam().value()).value(); + encoded_opt_value = + testutil::scaleEncodeAndCompareWithRef(GetParam().value()).value(); } // 'func' (lambda) @@ -183,7 +186,9 @@ TEST_P(ReadOutcomeParameterizedTest, ReadTest) { WasmSize value_size = 44; WasmSpan value_span = PtrSize(value_pointer, value_size).combine(); auto encoded_result = - scale::encode>(std::nullopt).value(); + testutil::scaleEncodeAndCompareWithRef>( + std::nullopt) + .value(); WasmOffset offset = 4; Buffer offset_value_data; @@ -194,7 +199,8 @@ TEST_P(ReadOutcomeParameterizedTest, ReadTest) { ASSERT_EQ(offset_value_data.size(), param.size() - offset); EXPECT_OUTCOME_TRUE( encoded_opt_offset_val_size, - scale::encode(std::make_optional(offset_value_data.size()))); + testutil::scaleEncodeAndCompareWithRef( + std::make_optional(offset_value_data.size()))); encoded_result = encoded_opt_offset_val_size; EXPECT_CALL( *memory_, diff --git a/test/core/host_api/crypto_extension_test.cpp b/test/core/host_api/crypto_extension_test.cpp index 772b770a8e..db38ba6e02 100644 --- a/test/core/host_api/crypto_extension_test.cpp +++ b/test/core/host_api/crypto_extension_test.cpp @@ -21,10 +21,12 @@ #include "mock/core/runtime/memory_mock.hpp" #include "mock/core/runtime/memory_provider_mock.hpp" #include "runtime/ptr_size.hpp" +#include "scale/kagome_scale.hpp" #include "scale/scale.hpp" #include "testutil/literals.hpp" #include "testutil/outcome.hpp" #include "testutil/prepare_loggers.hpp" +#include "testutil/scale_test_comparator.hpp" using namespace kagome::host_api; using kagome::common::Blob; @@ -127,9 +129,11 @@ class CryptoExtensionTest : public ::testing::Test { // scale-encoded string std::optional> optional_seed(seed); - seed_buffer.put(scale::encode(optional_seed).value()); + seed_buffer.put( + testutil::scaleEncodeAndCompareWithRef(optional_seed).value()); std::optional optional_mnemonic(mnemonic); - mnemonic_buffer.put(scale::encode(optional_mnemonic).value()); + mnemonic_buffer.put( + testutil::scaleEncodeAndCompareWithRef(optional_mnemonic).value()); sr25519_keypair = sr25519_provider_->generateKeypair(Sr25519Seed{seed}, {}); sr25519_signature = sr25519_provider_->sign(sr25519_keypair, input).value(); @@ -155,21 +159,22 @@ class CryptoExtensionTest : public ::testing::Test { secp_signature = secp256k1::RSVSignature::fromSpan(secp_signature_bytes).value(); - scale_encoded_secp_truncated_public_key = - Buffer(scale::encode(RecoverUncompressedPublicKeyReturnValue( - secp_truncated_public_key)) - .value()); + scale_encoded_secp_truncated_public_key = Buffer( + testutil::scaleEncodeAndCompareWithRef( + RecoverUncompressedPublicKeyReturnValue(secp_truncated_public_key)) + .value()); - scale_encoded_secp_compressed_public_key = - Buffer(scale::encode(RecoverCompressedPublicKeyReturnValue( - secp_compressed_pyblic_key)) - .value()); + scale_encoded_secp_compressed_public_key = Buffer( + testutil::scaleEncodeAndCompareWithRef( + RecoverCompressedPublicKeyReturnValue(secp_compressed_pyblic_key)) + .value()); // this value suits both compressed & uncompressed failure tests secp_invalid_signature_error = - Buffer(scale::encode(RecoverCompressedPublicKeyReturnValue( - kagome::crypto::secp256k1:: - secp256k1_verify_error::kInvalidSignature)) + Buffer(testutil::scaleEncodeAndCompareWithRef( + RecoverCompressedPublicKeyReturnValue( + kagome::crypto::secp256k1::secp256k1_verify_error:: + kInvalidSignature)) .value()); ed_public_keys_result diff --git a/test/core/host_api/offchain_extension_test.cpp b/test/core/host_api/offchain_extension_test.cpp index 4e689cde8f..dfcfb51af8 100644 --- a/test/core/host_api/offchain_extension_test.cpp +++ b/test/core/host_api/offchain_extension_test.cpp @@ -18,10 +18,12 @@ #include "offchain/types.hpp" #include "runtime/ptr_size.hpp" #include "scale/encode_append.hpp" +#include "scale/kagome_scale.hpp" #include "testutil/literals.hpp" #include "testutil/outcome.hpp" #include "testutil/outcome/dummy_error.hpp" #include "testutil/prepare_loggers.hpp" +#include "testutil/scale_test_comparator.hpp" using kagome::common::Buffer; using kagome::common::BufferView; @@ -111,7 +113,7 @@ class OffchainExtensionTest : public ::testing::Test { std::shared_ptr offchain_worker_; std::shared_ptr offchain_worker_pool_; - constexpr static uint32_t kU32Max = std::numeric_limits::max(); + static constexpr uint32_t kU32Max = std::numeric_limits::max(); }; /// For the tests where it is needed to check a valid behaviour no matter if @@ -164,7 +166,8 @@ TEST_F(OffchainExtensionTest, SubmitTransaction) { auto result_span = 44; EXPECT_CALL(*memory_, loadN(data_pointer, data_size)) - .WillOnce(Return(Buffer{scale::encode(xt).value()})); + .WillOnce( + Return(Buffer{testutil::scaleEncodeAndCompareWithRef(xt).value()})); EXPECT_CALL(*offchain_worker_, submitTransaction(_)) .WillOnce(Return(Success{})); EXPECT_CALL(*memory_, storeBuffer(_)).WillOnce(Return(result_span)); @@ -475,8 +478,8 @@ TEST_F(OffchainExtensionTest, HttpRequestWriteBody) { EXPECT_CALL(*memory_, loadN(chunk_pointer, chunk_size)) .WillOnce(Return(chunk)); EXPECT_CALL(*memory_, loadN(deadline_pointer, deadline_size)) - .WillOnce( - Return(Buffer{gsl::make_span(scale::encode(deadline_opt).value())})); + .WillOnce(Return(Buffer{gsl::make_span( + testutil::scaleEncodeAndCompareWithRef(deadline_opt).value())})); EXPECT_CALL(*offchain_worker_, httpRequestWriteBody(id, chunk, deadline_opt)) .WillOnce(Return(result)); @@ -521,8 +524,8 @@ TEST_F(OffchainExtensionTest, HttpResponseWait) { EXPECT_CALL(*memory_, loadN(ids_pointer, ids_size)) .WillOnce(Return(Buffer{gsl::make_span(scale::encode(ids).value())})); EXPECT_CALL(*memory_, loadN(deadline_pointer, deadline_size)) - .WillOnce( - Return(Buffer{gsl::make_span(scale::encode(deadline_opt).value())})); + .WillOnce(Return(Buffer{gsl::make_span( + testutil::scaleEncodeAndCompareWithRef(deadline_opt).value())})); EXPECT_CALL(*offchain_worker_, httpResponseWait(ids, deadline_opt)) .WillOnce(Return(result)); @@ -639,7 +642,8 @@ TEST_F(OffchainExtensionTest, SetAuthNodes) { WasmSize nodes_pos_size = 43; std::vector nodes{Buffer("asd"_peerid.toVector())}; EXPECT_CALL(*memory_, loadN(nodes_pos_pointer, nodes_pos_size)) - .WillOnce(Return(Buffer{gsl::make_span(scale::encode(nodes).value())})); + .WillOnce(Return(Buffer{gsl::make_span( + testutil::scaleEncodeAndCompareWithRef(nodes).value())})); EXPECT_CALL(*offchain_worker_, setAuthorizedNodes(_, true)) .WillOnce(Return()); offchain_extension_->ext_offchain_set_authorized_nodes_version_1( diff --git a/test/core/host_api/storage_extension_test.cpp b/test/core/host_api/storage_extension_test.cpp index 38d1b21eab..858dc36d71 100644 --- a/test/core/host_api/storage_extension_test.cpp +++ b/test/core/host_api/storage_extension_test.cpp @@ -18,11 +18,13 @@ #include "mock/core/storage/trie/trie_batches_mock.hpp" #include "runtime/ptr_size.hpp" #include "scale/encode_append.hpp" +#include "scale/kagome_scale.hpp" #include "storage/predefined_keys.hpp" #include "testutil/literals.hpp" #include "testutil/outcome.hpp" #include "testutil/outcome/dummy_error.hpp" #include "testutil/prepare_loggers.hpp" +#include "testutil/scale_test_comparator.hpp" using kagome::common::Buffer; using kagome::common::BufferView; @@ -37,10 +39,10 @@ using kagome::runtime::WasmOffset; using kagome::runtime::WasmPointer; using kagome::runtime::WasmSize; using kagome::runtime::WasmSpan; -using kagome::storage::trie::TrieBatchMock; using kagome::storage::trie::PolkadotCodec; using kagome::storage::trie::PolkadotTrieCursorMock; using kagome::storage::trie::RootHash; +using kagome::storage::trie::TrieBatchMock; using ::testing::_; using ::testing::Invoke; @@ -74,7 +76,7 @@ class StorageExtensionTest : public ::testing::Test { std::shared_ptr storage_extension_; PolkadotCodec codec_; - constexpr static uint32_t kU32Max = std::numeric_limits::max(); + static constexpr uint32_t kU32Max = std::numeric_limits::max(); }; /// For the tests where it is needed to check a valid behaviour no matter if @@ -303,7 +305,8 @@ TEST_P(OutcomeParameterizedTest, StorageReadTest) { ASSERT_EQ(offset_value_data.size(), value_data.size() - offset); EXPECT_OUTCOME_TRUE( encoded_opt_offset_val_size, - scale::encode(std::make_optional(offset_value_data.size()))); + testutil::scaleEncodeAndCompareWithRef( + std::make_optional(offset_value_data.size()))); WasmSpan res_wasm_span = 1337; // expect key loaded, then data stored @@ -341,11 +344,13 @@ TEST_F(StorageExtensionTest, ExtStorageAppendTest) { Buffer key_data(key.size, 'k'); Buffer value_data1(42, '1'); - Buffer value_data1_encoded{scale::encode(value_data1).value()}; + Buffer value_data1_encoded{ + testutil::scaleEncodeAndCompareWithRef(value_data1).value()}; PtrSize value1(42, value_data1_encoded.size()); Buffer value_data2(43, '2'); - Buffer value_data2_encoded{scale::encode(value_data2).value()}; + Buffer value_data2_encoded{ + testutil::scaleEncodeAndCompareWithRef(value_data2).value()}; PtrSize value2(45, value_data2_encoded.size()); // @given wasm memory that can provide these key and values @@ -366,7 +371,7 @@ TEST_F(StorageExtensionTest, ExtStorageAppendTest) { // @then storage is inserted by scale encoded vector containing // EncodeOpaqueValue with value1 vals.push_back(scale::EncodeOpaqueValue{value_data1_encoded.asVector()}); - vals_encoded = Buffer(scale::encode(vals).value()); + vals_encoded = Buffer(testutil::scaleEncodeAndCompareWithRef(vals).value()); EXPECT_CALL(*trie_batch_, put(key_data.view(), vals_encoded)) .WillOnce(Return(outcome::success())); @@ -382,7 +387,7 @@ TEST_F(StorageExtensionTest, ExtStorageAppendTest) { // @then storage is inserted by scale encoded vector containing two // EncodeOpaqueValues with value1 and value2 vals.push_back(scale::EncodeOpaqueValue{value_data2_encoded.asVector()}); - vals_encoded = Buffer(scale::encode(vals).value()); + vals_encoded = Buffer(testutil::scaleEncodeAndCompareWithRef(vals).value()); EXPECT_CALL(*trie_batch_, put(key_data.view(), vals_encoded)) .WillOnce(Return(outcome::success())); @@ -397,11 +402,13 @@ TEST_F(StorageExtensionTest, ExtStorageAppendTestCompactLenChanged) { Buffer key_data(key.size, 'k'); Buffer value_data1(42, '1'); - Buffer value_data1_encoded{scale::encode(value_data1).value()}; + Buffer value_data1_encoded{ + testutil::scaleEncodeAndCompareWithRef(value_data1).value()}; PtrSize value1(42, value_data1_encoded.size()); Buffer value_data2(43, '2'); - Buffer value_data2_encoded{scale::encode(value_data2).value()}; + Buffer value_data2_encoded{ + testutil::scaleEncodeAndCompareWithRef(value_data2).value()}; PtrSize value2(45, value_data2_encoded.size()); // @given wasm memory that can provide these key and values @@ -415,7 +422,8 @@ TEST_F(StorageExtensionTest, ExtStorageAppendTestCompactLenChanged) { std::vector vals( scale::compact::EncodingCategoryLimits::kMinUint16 - 1, scale::EncodeOpaqueValue{value_data1_encoded.asVector()}); - Buffer vals_encoded = Buffer(scale::encode(vals).value()); + Buffer vals_encoded = + Buffer(testutil::scaleEncodeAndCompareWithRef(vals).value()); { // @when encoded vals is stored by given key @@ -424,7 +432,7 @@ TEST_F(StorageExtensionTest, ExtStorageAppendTestCompactLenChanged) { // @when storage is inserted by one more value by the same key vals.push_back(scale::EncodeOpaqueValue{value_data2_encoded.asVector()}); - vals_encoded = Buffer(scale::encode(vals).value()); + vals_encoded = Buffer(testutil::scaleEncodeAndCompareWithRef(vals).value()); // @then everything fine: storage is inserted with vals with new value EXPECT_CALL(*trie_batch_, put(key_data.view(), vals_encoded)) @@ -444,7 +452,7 @@ TEST_F(StorageExtensionTest, ExtStorageAppendTestCompactLenChanged) { */ TEST_P(BuffersParametrizedTest, Blake2_256_EnumeratedTrieRoot) { auto &[values, hash_array] = GetParam(); - auto values_enc = scale::encode(values).value(); + auto values_enc = testutil::scaleEncodeAndCompareWithRef(values).value(); using testing::_; PtrSize values_span{42, static_cast(values_enc.size())}; @@ -475,7 +483,7 @@ TEST_P(BuffersParametrizedTest, Blake2_256_OrderedTrieRootV1) { WasmSpan values_data = PtrSize(values_ptr, values_size).combine(); WasmPointer result = 1984; - Buffer buffer{scale::encode(values).value()}; + Buffer buffer{testutil::scaleEncodeAndCompareWithRef(values).value()}; EXPECT_CALL(*memory_, loadN(values_ptr, values_size)) .WillOnce(Return(buffer)); @@ -519,7 +527,9 @@ TEST_F(StorageExtensionTest, StorageGetV1Test) { WasmSpan key_span = PtrSize(key_pointer, key_size).combine(); Buffer value(8, 'v'); - auto encoded_opt_value = scale::encode>(value).value(); + auto encoded_opt_value = + testutil::scaleEncodeAndCompareWithRef>(value) + .value(); // expect key and value were loaded EXPECT_CALL(*memory_, loadN(key_pointer, key_size)).WillOnce(Return(key)); @@ -593,7 +603,9 @@ TEST_F(StorageExtensionTest, ExtStorageClearPrefixV2Test) { WasmSize limit_size = 43; uint32_t limit{22}; WasmSpan limit_span = PtrSize(limit_pointer, limit_size).combine(); - Buffer encoded_opt_limit{scale::encode(std::make_optional(limit)).value()}; + Buffer encoded_opt_limit{ + testutil::scaleEncodeAndCompareWithRef(std::make_optional(limit)) + .value()}; EXPECT_CALL(*memory_, loadN(prefix_pointer, prefix_size)) .WillOnce(Return(prefix)); @@ -605,7 +617,7 @@ TEST_F(StorageExtensionTest, ExtStorageClearPrefixV2Test) { clearPrefix(prefix.view(), std::make_optional(limit))) .WillOnce(Return(outcome::success(result))); - auto enc_result = scale::encode(result).value(); + auto enc_result = testutil::scaleEncodeAndCompareWithRef(result).value(); WasmPointer result_pointer = 43; WasmSize result_size = 43; WasmSpan result_span = PtrSize(result_pointer, result_size).combine(); @@ -654,7 +666,7 @@ TEST_F(StorageExtensionTest, Blake2_256_TrieRootV1) { WasmSpan dict_data = PtrSize(values_ptr, values_size).combine(); WasmPointer result = 1984; - Buffer buffer{scale::encode(dict).value()}; + Buffer buffer{testutil::scaleEncodeAndCompareWithRef(dict).value()}; EXPECT_CALL(*memory_, loadN(values_ptr, values_size)) .WillOnce(Return(buffer)); diff --git a/test/core/network/rpc_libp2p_test.cpp b/test/core/network/rpc_libp2p_test.cpp index 21d11e6999..4a057e7bba 100644 --- a/test/core/network/rpc_libp2p_test.cpp +++ b/test/core/network/rpc_libp2p_test.cpp @@ -12,10 +12,12 @@ #include "mock/libp2p/host/host_mock.hpp" #include "network/helpers/scale_message_read_writer.hpp" #include "network/types/blocks_response.hpp" +#include "scale/kagome_scale.hpp" #include "scale/scale.hpp" #include "testutil/libp2p/message_read_writer_helper.hpp" #include "testutil/literals.hpp" #include "testutil/prepare_loggers.hpp" +#include "testutil/scale_test_comparator.hpp" using namespace kagome; using namespace network; @@ -63,8 +65,10 @@ class RpcLibp2pTest : public testing::Test { // request and response BlocksResponse request_{.blocks = {primitives::BlockData{}}}; BlocksResponse response_{.blocks = {primitives::BlockData{}}}; - kagome::common::Buffer encoded_request_{scale::encode(request_).value()}; - kagome::common::Buffer encoded_response_{scale::encode(response_).value()}; + kagome::common::Buffer encoded_request_{ + testutil::scaleEncodeAndCompareWithRef(request_).value()}; + kagome::common::Buffer encoded_response_{ + testutil::scaleEncodeAndCompareWithRef(response_).value()}; }; /** diff --git a/test/core/runtime/executor_test.cpp b/test/core/runtime/executor_test.cpp index c7e09813b6..1354a65dca 100644 --- a/test/core/runtime/executor_test.cpp +++ b/test/core/runtime/executor_test.cpp @@ -19,10 +19,13 @@ #include "mock/core/runtime/trie_storage_provider_mock.hpp" #include "mock/core/storage/trie/trie_batches_mock.hpp" #include "mock/core/storage/trie/trie_storage_mock.hpp" +#include "scale/encode_append.hpp" +#include "scale/kagome_scale.hpp" #include "testutil/literals.hpp" #include "testutil/outcome.hpp" #include "testutil/prepare_loggers.hpp" #include "testutil/runtime/common/basic_code_provider.hpp" +#include "testutil/scale_test_comparator.hpp" using kagome::blockchain::BlockHeaderRepository; using kagome::blockchain::BlockHeaderRepositoryMock; @@ -132,7 +135,7 @@ class ExecutorTest : public testing::Test { EXPECT_CALL(*module_repo_, getInstanceAt(blockchain_state, storage_state)) .WillRepeatedly(testing::Return(module_instance)); - Buffer enc_res{scale::encode(res).value()}; + Buffer enc_res{testutil::scaleEncodeAndCompareWithRef(res).value()}; EXPECT_CALL(*memory_, loadN(RESULT_LOCATION.ptr, RESULT_LOCATION.size)) .WillOnce(Return(enc_res)); } @@ -152,15 +155,14 @@ TEST_F(ExecutorTest, LatestStateSwitchesCorrectly) { kagome::primitives::BlockInfo block_info2{43, "block_hash2"_hash256}; kagome::primitives::BlockInfo block_info3{44, "block_hash3"_hash256}; - Buffer enc_args{scale::encode(2, 3).value()}; + Buffer enc_args{testutil::scaleEncodeAndCompareWithRef(2, 3).value()}; prepareCall( block_info1, "state_hash1"_hash256, CallType::Persistent, enc_args, 5); - auto ctx = - ctx_factory_->persistentAt(block_info1.hash, std::nullopt).value(); + auto ctx = ctx_factory_->persistentAt(block_info1.hash, std::nullopt).value(); auto res = executor.decodedCallWithCtx(ctx, "addTwo", 2, 3).value(); EXPECT_EQ(res, 5); - enc_args = scale::encode(7, 10).value(); + enc_args = testutil::scaleEncodeAndCompareWithRef(7, 10).value(); prepareCall( block_info1, "state_hash2"_hash256, CallType::Ephemeral, enc_args, 17); EXPECT_OUTCOME_TRUE( @@ -169,14 +171,14 @@ TEST_F(ExecutorTest, LatestStateSwitchesCorrectly) { block_info1.hash, "state_hash2"_hash256, "addTwo", 7, 10)); ASSERT_EQ(res2, 17); - enc_args = scale::encode(0, 0).value(); + enc_args = testutil::scaleEncodeAndCompareWithRef(0, 0).value(); prepareCall( block_info1, "state_hash2"_hash256, CallType::Persistent, enc_args, 0); auto ctx3 = ctx_factory_->persistentAt(block_info1.hash, std::nullopt).value(); EXPECT_EQ(executor.decodedCallWithCtx(ctx, "addTwo", 0, 0).value(), 0); - enc_args = scale::encode(7, 10).value(); + enc_args = testutil::scaleEncodeAndCompareWithRef(7, 10).value(); prepareCall( block_info1, "state_hash3"_hash256, CallType::Ephemeral, enc_args, 17); EXPECT_OUTCOME_TRUE( @@ -185,15 +187,14 @@ TEST_F(ExecutorTest, LatestStateSwitchesCorrectly) { block_info1.hash, "state_hash3"_hash256, "addTwo", 7, 10)); ASSERT_EQ(res4, 17); - enc_args = scale::encode(-5, 5).value(); + enc_args = testutil::scaleEncodeAndCompareWithRef(-5, 5).value(); prepareCall( block_info2, "state_hash4"_hash256, CallType::Persistent, enc_args, 0); auto ctx5 = ctx_factory_->persistentAt(block_info2.hash, std::nullopt).value(); - EXPECT_EQ(executor.decodedCallWithCtx(ctx5, "addTwo", -5, 5).value(), - 0); + EXPECT_EQ(executor.decodedCallWithCtx(ctx5, "addTwo", -5, 5).value(), 0); - enc_args = scale::encode(7, 10).value(); + enc_args = testutil::scaleEncodeAndCompareWithRef(7, 10).value(); prepareCall( block_info2, "state_hash5"_hash256, CallType::Ephemeral, enc_args, 17); EXPECT_OUTCOME_TRUE( diff --git a/test/core/storage/trie_pruner/trie_pruner_test.cpp b/test/core/storage/trie_pruner/trie_pruner_test.cpp index 49949e12c5..c238bcb7d3 100644 --- a/test/core/storage/trie_pruner/trie_pruner_test.cpp +++ b/test/core/storage/trie_pruner/trie_pruner_test.cpp @@ -626,7 +626,7 @@ TEST_F(TriePrunerTest, RestoreStateFromGenesis) { ON_CALL(*block_tree, getChildren(_)) .WillByDefault(Return(std::vector{})); - + ON_CALL(*block_tree, bestLeaf()) .WillByDefault(Return(BlockInfo{6, hash_from_header(headers.at(6))})); @@ -848,3 +848,54 @@ TEST_F(TriePrunerTest, FastSyncScenario) { ASSERT_OUTCOME_SUCCESS_TRY(pruner->pruneFinalized(headers[n])); } } + +#include "scale/encoder/primitives.hpp" + +struct TTT { + SCALE_TIE(6); + + uint8_t q1; + std::string q2; + size_t q3; + uint32_t q4; + uint8_t q5; + size_t q6; +}; + +TEST_F(TriePrunerTest, NewScale) { + std::vector data = {{ + .q1 = 100, + .q2 = "TEST DATA ENCRYPTED", + .q3 = 0xcdff, + .q4 = 0xe7ffffff, + .q5 = 250, + .q6 = 0xffffffffffffffff, + }, + { + .q1 = 150, + .q2 = "TEST DATA ENCRYPTED - 222", + .q3 = 0xceff, + .q4 = 0xe7eeeeff, + .q5 = 250, + .q6 = 0xffffffeeeeffffff, + }}; + + std::map> data_map; + data_map["KEY"] = std::move(data); + + std::vector data_0; + kagome::scale::encode( + [&](const uint8_t *const val, size_t count) { + for (size_t i = 0; i < count; ++i) { + data_0.emplace_back(val[i]); + } + }, + data_map); + + auto data_1 = scale::encode(data_map).value(); + + ASSERT_EQ(data_0.size(), data_1.size()); + for (size_t ix = 0; ix < data_0.size(); ++ix) { + ASSERT_EQ(data_0[ix], data_1[ix]); + } +} diff --git a/test/testutil/scale_test_comparator.hpp b/test/testutil/scale_test_comparator.hpp new file mode 100644 index 0000000000..cd54ff46be --- /dev/null +++ b/test/testutil/scale_test_comparator.hpp @@ -0,0 +1,37 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef KAGOME_SCALE_TEST_COMPARATOR_HPP +#define KAGOME_SCALE_TEST_COMPARATOR_HPP + +#include "scale/kagome_scale.hpp" +#include "scale/scale.hpp" + +namespace testutil { + + template + inline outcome::result> scaleEncodeAndCompareWithRef( + T &&...t) { + std::vector data_0; + kagome::scale::encode( + [&](const uint8_t *const val, size_t count) { + for (size_t i = 0; i < count; ++i) { + data_0.emplace_back(val[i]); + } + }, + t...); + + std::vector data_1 = ::scale::encode(t...).value(); + assert(data_0.size() == data_1.size()); + for (size_t ix = 0; ix < data_0.size(); ++ix) { + assert(data_0[ix] == data_1[ix]); + } + + return outcome::success(data_0); + } + +} // namespace testutil + +#endif // KAGOME_SCALE_TEST_COMPARATOR_HPP