diff --git a/.gitignore b/.gitignore index cd7729a..4933a2d 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,8 @@ .idea /build +/cmake-build-* + /.build /.venv /.vcpkg diff --git a/python/asn1.py b/python/asn1.py index 9bdee29..09d6398 100755 --- a/python/asn1.py +++ b/python/asn1.py @@ -4,11 +4,8 @@ import os.path import sys - DIR = os.path.dirname(__file__) TEST_VECTORS_DIR = os.path.join(DIR, "../test-vectors") -OUTPUT_NAME = "types" - def flatten(aaa: list[list]): return [a for aa in aaa for a in aa] @@ -125,11 +122,10 @@ def c_scale(ty: Type, encode: list[str], decode: list[str]): " return s;", "}", *ty.c_tdecl(), - "scale::ScaleDecoderStream &operator>>(scale::ScaleDecoderStream &s, %s &) = delete;" - % ty.c_tname(), - "void decodeConfig(scale::ScaleDecoderStream &s, %s &v, const auto &config) {" + "inline scale::ScaleDecoderStream &operator>>(scale::ScaleDecoderStream &s, %s &v) {" % ty.c_tname(), *indent(decode), + " return s;", "}", ] @@ -138,7 +134,7 @@ def c_scale_struct(ty: Type, members: list[str]): return c_scale( ty, ["s << v.%s;" % x for x in members], - ["decodeConfig(s, v.%s, config);" % x for x in members], + ["s >> v.%s;" % x for x in members], ) @@ -151,30 +147,63 @@ def c_diff(NS: str, ty: Type, lines: list[str]): ] -def parse_types(NS: str, ARGS: list[str], path: str, key: str): +def c_fittest_int_type(min_value: int, max_value: int): + if min_value <= max_value: + if min_value >= 0: + if max_value <= 255: return "uint8_t" + if max_value <= 65536: return "uint16_t" + if max_value <= 4294967295: return "uint32_t" + if max_value <= 18446744073709551615: return "uint64_t" + else: # min_value < 0: + if min_value >= -128 and max_value <= 127: return "int8_t" + if min_value >= -32768 and max_value <= 32767: return "int16_t" + if min_value >= -2147483648 and max_value <= 2147483647: return "int32_t" + if min_value >= -9223372036854775808 and max_value <= 9223372036854775807: return "int64_t" + return None + + +def parse_types(cpp_namespace: str, ARGS: list[str], path: str, key: str, imports: list[str] = []): def asn_sequence_of(t): (size,) = t.get("size", (None,)) fixed = isinstance(size, (int, str)) T = t["element"]["type"] - assert T in asn_types + assert T in types if T == "U8": if fixed: - return "qtils::BytesN<%s>" % c_dash(size) + if type(size) is int: + return "qtils::BytesN<%u>" % size + else: + return "::jam::ConfigVec" % c_dash(size) return "qtils::Bytes" if fixed: if isinstance(size, str): - return "jam::ConfigVec<%s, ConfigField::%s>" % (T, c_dash(size)) + return "::jam::ConfigVec<%s, Config::Field::%s>" % (T, c_dash(size)) return "std::array<%s, %s>" % (T, c_dash(size)) return "std::vector<%s>" % T def asn_member(t): - if t["type"] == "OCTET STRING": - t = dict(type="SEQUENCE OF", element=dict(type="U8"), size=t["size"]) + if t["type"] == "INTEGER": + int_type = c_fittest_int_type(*t["restricted-to"][0]) + if int_type is None: raise TypeError(t) + return int_type if t["type"] == "BOOLEAN": return "bool" - if t["type"] == "SEQUENCE OF": + if t["type"] == "OCTET STRING": + if 'size' in t: + t = dict(type="SEQUENCE OF", element=dict(type="U8"), size=t["size"]) + else: + t = dict(type="SEQUENCE OF", element=dict(type="U8")) + if t["type"] == "NULL": + if "tag" in t: + if "number" in t["tag"]: + r = "Tagged" % t["tag"]["number"] + else: + r = "Tagged" % c_dash(t["tag"]["str"]) + else: + r = "Empty" + elif t["type"] == "SEQUENCE OF": r = asn_sequence_of(t) - elif t["type"] in asn_types: + elif t["type"] in types: r = types[t["type"]].c_tname() else: raise TypeError(t) @@ -183,81 +212,66 @@ def asn_member(t): return r asn_types: dict = asn1tools.parse_files([path])[key]["types"] - if "U8" not in asn_types: - asn_types["U8"] = dict(type="INTEGER") deps1, deps2 = asn_deps(asn_types) + types = {tname: Type(tname) for tname in asn_types} + types.update({tname: Type(tname) for tname in imports}) for tname, args in asn_args(ARGS, asn_types, deps2).items(): types[tname].args = args + enum_trait = [] for tname, t in asn_types.items(): ty = types[tname] - if tname == "BOOLEAN": - ty.decl = c_using(tname, "bool") - continue - if tname == "U8": - ty.decl = c_using(tname, "uint8_t") - continue - if tname == "U16": - ty.decl = c_using(tname, "uint16_t") - continue - if tname == "U32": - ty.decl = c_using(tname, "uint32_t") - continue - if t["type"] == "NULL": - t = dict(type="SEQUENCE", members=[]) if t["type"] == "CHOICE": - if tname == "MmrPeak" or tname == "AvailabilityAssignmentItem": # TODO(#14): Need to make in universal - assert [x["name"] for x in t["members"]] == ["none", "some"] - ty.decl = c_using( - tname, "std::optional<%s>" % asn_member(t["members"][1]) - ) - continue - ty.decl = c_struct( - tname, - [ - ( - "v", - "boost::variant<%s>" - % ", ".join(asn_member(x) for x in t["members"]), + if [x["name"] for x in t["members"]] == ["none", "some"]: + if [x["tag"]["number"] for x in t["members"]] == [0, 1]: + x = t["members"][1] + ty.decl = c_using( + tname, + "std::optional<%s>" % asn_member(t["members"][1]) ) - ], - ) - ty.scale += c_scale_struct(ty, ["v"]) - ty.diff = c_diff( - NS, - ty, - [ - "diff(indent, v1.v, v2.v, {", - *(' "%s",' % c_dash(x["name"]) for x in t["members"]), - "});", - ], - ) + continue + ty.decl = [ + "using %s = Tagged, struct %s_Tag>;" % tname + ] + ty.diff = [ + "DIFF_F(%s::%s) {" % (cpp_namespace, tname), + " static constexpr std::string_view tags[] = {", + *[' "%s",' % x["name"] for x in t["members"][:-1]], + *[' "%s"' % x["name"] for x in t["members"][-1:]], + " };", + " diff_v(indent, v1, v2, std::span(tags));", + "}" + ] continue if t["type"] == "ENUMERATED": - values = [x[1] for x in t["values"]] - assert all(x == i and x < 0xFF for i, x in enumerate(values)) + base_type = c_fittest_int_type(min((x for _, x in t["values"]), default=0), max((x for _, x in t["values"]), default=0)) + if base_type is None: raise TypeError(t) ty.decl = [ - "enum class %s : uint8_t {" % tname, - *(" %s," % c_dash(x[0]) for x in t["values"]), + "enum class %s : %s {" % (tname, base_type), + *(" %s = %d," % (c_dash(name), index) for name, index in t["values"][:-1]), + *(" %s = %d" % (c_dash(name), index) for name, index in t["values"][-1:]), "};", ] - ty.diff = c_diff( - NS, - ty, - [ - "diff(indent, v1, v2, {", - *(' "%s",' % c_dash(x[0]) for x in t["values"]), - "});", - ], - ) + ty.diff = [ + "DIFF_F(%s::%s) {" % (cpp_namespace, tname), + " static const std::unordered_map<%s, std::string_view> names = {" % base_type, + *(' {%d, "%s"},' % (num, name) for name, num in t["values"][:-1]), + *(' {%d, "%s"}' % (num, name) for name, num in t["values"][-1:]), + " };", + " diff_e(indent, v1, v2, names);", + "}", + ] enum_trait.append( "SCALE_DEFINE_ENUM_VALUE_LIST(%s, %s, %s)" % ( - NS, + cpp_namespace, ty.name, ", ".join( - "%s::%s::%s" % (NS, ty.name, c_dash(x[0])) for x in t["values"] + "%s::%s::%s" % (cpp_namespace, ty.name, c_dash(x[0])) for x in t["values"] ), ) ) @@ -268,12 +282,15 @@ def asn_member(t): ) ty.scale += c_scale_struct(ty, [c_dash(x["name"]) for x in t["members"]]) ty.diff = c_diff( - NS, ty, ["DIFF_M(%s);" % c_dash(x["name"]) for x in t["members"]] + cpp_namespace, ty, ["DIFF_M(%s);" % c_dash(x["name"]) for x in t["members"]] ) ty.diff = c_diff( - NS, ty, ["DIFF_M(%s);" % c_dash(x["name"]) for x in t["members"]] + cpp_namespace, ty, ["DIFF_M(%s);" % c_dash(x["name"]) for x in t["members"]] ) continue + if t["type"] == "NULL": + ty.decl = c_using(tname, "Empty"); + continue ty.decl = c_using(tname, asn_member(t)) order = asn1tools.c.utils.topological_sort(deps1) @@ -286,101 +303,273 @@ def parse_const(path: str, key: str): return {k: v["value"] for k, v in values.items()} -class Gen: - def __init__(self, NS: str, ARGS: list[str], path: str, key: str, configs): - g_config = [ - "struct ConfigField {", - *[" struct %s {};" % c_dash(a) for a in ARGS], - "};", - "struct Config {", - *[" uint32_t %s;" % c_dash(a) for a in ARGS], +class GenConstants: + def __init__(self, cpp_namespace: str, path: str): + config_asn = [ + (name, parse_const(asn_file("%s/%s-const" % (path, name)), "Constants")) + for name in ["tiny", "full"] + ] + + constants = dict((c_dash(a), dict([(c_dash(k), v) for (k, v) in b.items()])) for (a, b) in config_asn) + + set_names = [x for x in constants.keys()] + constant_names = [c_dash(x) for x in next(iter(constants.values())).keys()] + + self.struct = [ + "// Auto-generated file", + "", + "#pragma once", + "", + "#include ", + "", + "namespace %s {" % cpp_namespace, + "" + " struct Config {", + "", + " struct Field {", + *[" struct %s {};" % const_name for const_name in constant_names], + " };", + *[" uint32_t %s;" % const_name for const_name in constant_names], *[ - " auto get(ConfigField::%s) const { return %s; }" - % (c_dash(a), c_dash(a)) - for a in ARGS + " auto get(Field::%s) const { return %s; }" + % (const_name, const_name) + for const_name in constant_names ], - "};", + " };", + "", + "} // namespace %s" % cpp_namespace ] - for name, args in configs: - g_config += [ - "constexpr Config config_%s {" % name, - *[" .%s = %s," % (c_dash(a), args[a]) for a in ARGS], + + self.constants = dict() + self.configs = dict() + for (set_name, pairs) in constants.items(): + content = [ + "// Auto-generated file", + "", + "#pragma once", + "", + "#include ", + "", + "namespace %s::constants::%s {" % (cpp_namespace, set_name), + "" + ] + content += [" constexpr uint32_t %s = %d;" % (name, value) for (name, value) in pairs.items()] + content += [ + "", + "} // namespace %s" % cpp_namespace + ] + self.constants[set_name] = content + + content = [ + "// Auto-generated file", + "", + "#pragma once", + "", + "#include ", + "#include " % set_name, + "", + "namespace %s::config {" % cpp_namespace, + "", + " constexpr Config %s {" % set_name, + *[" .%s = constants::%s::%s," % (name, set_name, name) for name in constant_names], + " };", + "", "};", ] - self.types, self.enum_trait = parse_types(NS, ARGS, path, key) + self.configs[set_name] = content + + def write(self): + prefix = os.path.join(TEST_VECTORS_DIR) + write(prefix + "/config.hpp", self.struct) + for (set_name, content) in self.constants.items(): + write(prefix + "/constants-%s.hpp" % set_name, content) + for (set_name, content) in self.configs.items(): + write(prefix + "/config-%s.hpp" % set_name, content) + + +class GenCommonTypes: + def __init__(self, cpp_namespace: str, path: str): + self.types, self.enum_trait = parse_types(cpp_namespace, [], asn_file("%s/jam-types" % path), "JamTypes") self.g_types = flatten([*ty.c_tdecl(), *ty.decl] for ty in self.types) - self.g_types = g_config + self.g_types - self.g_types = ["namespace %s {" % NS, *indent(self.g_types), "}"] + self.g_types = ["namespace %s {" % cpp_namespace, *indent(self.g_types), "}"] self.g_types = [ + "// Auto-generated file", + "", "#pragma once", + "", + "#include ", + "#include ", + "#include ", + "#include ", + "#include ", "#include ", + "", "#include ", + "#include ", + "#include ", + "#include ", + "", *self.g_types, ] - self.g_scale = flatten(ty.scale for ty in self.types) - self.g_scale = ["namespace %s {" % NS, *indent(self.g_scale), "}"] + self.g_scale = flatten([ty.scale for ty in self.types]) + self.g_scale = [ + "namespace %s {" % cpp_namespace, + *indent(self.g_scale), "}" + ] self.g_scale = [ + "// Auto-generated file", + "", "#pragma once", + "", "#include ", + "", "#include ", - '#include "%s.hpp"' % OUTPUT_NAME, + '#include ', + "", *self.g_scale, *self.enum_trait, ] - self.g_diff = flatten(ty.diff for ty in self.types) + self.g_diff = flatten([ty.diff for ty in self.types]) self.g_diff = [ + "// Auto-generated file", + "", "#pragma once", + "", "#include ", - '#include "%s.hpp"' % OUTPUT_NAME, + '#include ', + "", + *self.g_diff, + ] + + def write(self): + prefix = os.path.join(TEST_VECTORS_DIR) + write(prefix + "/common-types.hpp", self.g_types) + write(prefix + "/common-scale.hpp", self.g_scale) + write(prefix + "/common-diff.hpp", self.g_diff) + + +class GenSpecialTypes: + def __init__(self, cpp_namespace: str, name: str, path: str, module: str, configs: list[str]): + self.asn_file = asn_file(path) + + asn_imports: dict = asn1tools.parse_files([self.asn_file])[module]["imports"] + + includes = [] + usings = [] + for module_name, importing_types in asn_imports.items(): + match module_name: + case 'JamTypes': + includes += ["#include "] + usings += ["using ::%s::%s;" % (cpp_namespace, t) for t in importing_types] + + self.types, self.enum_trait = parse_types("%s::%s" % (cpp_namespace, name), [], self.asn_file, module, + flatten(asn_imports.values())) + self.g_types = flatten([[*ty.c_tdecl(), *ty.decl] for ty in self.types]) + self.g_types = ["namespace %s::%s {" % (cpp_namespace, name), "", *indent(usings), "", *indent(self.g_types), "", "}"] + self.g_types = [ + "// Auto-generated file", + "", + "#pragma once", + "", + "#include ", + "#include ", + "#include ", + "#include ", + + "#include ", + "#include ", + "", + "#include ", + "#include ", + *includes, + "", + *self.g_types, + ] + self.g_scale = flatten([ty.scale for ty in self.types]) + self.g_scale = ["namespace %s::%s {" % (cpp_namespace, name), "", *indent(self.g_scale), "", "}"] + self.g_scale = [ + "// Auto-generated file", + "", + "#pragma once", + "", + "#include ", + "", + '#include ', + '#include ' % (name, name), + "", + *self.g_scale, + *self.enum_trait, + ] + self.g_diff = flatten([ty.diff for ty in self.types]) + self.g_diff = [ + "// Auto-generated file", + "", + "#pragma once", + "", + "#include ", + '#include ', + '#include ' % (name, name), + "", *self.g_diff, ] def write(self, name: str): - prefix = os.path.join(TEST_VECTORS_DIR, name, OUTPUT_NAME) - write(prefix + ".hpp", self.g_types) - write(prefix + ".scale.hpp", self.g_scale) - write(prefix + ".diff.hpp", self.g_diff) + prefix = os.path.join(TEST_VECTORS_DIR, name, name) + write(prefix + "-types.hpp", self.g_types) + write(prefix + "-scale.hpp", self.g_scale) + write(prefix + "-diff.hpp", self.g_diff) -def safrole(): - g = Gen( - "jam::test_vectors_safrole", - ["validators-count", "epoch-length"], - asn_file("safrole/safrole"), - "SafroleModule", - [ - (name, parse_const(asn_file("safrole/%s" % name), "Constants")) - for name in ["tiny", "full"] - ], +def constants(): + g = GenConstants( + "jam::test_vectors", + "jam-types-asn", ) - g.write("safrole") + g.write() -def disputes(): - g = Gen( - "jam::test_vectors_disputes", - ["validators-count", "core-count", "epoch-length", "validators-super-majority"], - asn_file("disputes/disputes"), - "DisputesModule", - [ - (name, parse_const(asn_file("disputes/%s" % name), "Constants")) - for name in ["tiny", "full"] - ], + +def types(): + g = GenCommonTypes( + "jam::test_vectors", + "jam-types-asn", ) - g.write("disputes") + g.write() def history(): - g = Gen( - "jam::test_vectors_history", - [], - asn_file("history/history"), + g = GenSpecialTypes( + "jam::test_vectors", + "history", + "history/history", "HistoryModule", [], ) g.write("history") +def safrole(): + g = GenSpecialTypes( + "jam::test_vectors", + "safrole", + "safrole/safrole", + "SafroleModule", + ["tiny", "full"], + ) + g.write("safrole") + + +def disputes(): + g = GenSpecialTypes( + "jam::test_vectors", + "disputes", + "disputes/disputes", + "DisputesModule", + ["tiny", "full"], + ) + g.write("disputes") + + if __name__ == "__main__": for arg in sys.argv[1:]: - dict(safrole=safrole, history=history, disputes=disputes)[arg]() + dict(constants=constants, types=types, history=history, safrole=safrole, disputes=disputes)[arg]() diff --git a/src/jam/empty.hpp b/src/jam/empty.hpp new file mode 100644 index 0000000..e13694b --- /dev/null +++ b/src/jam/empty.hpp @@ -0,0 +1,27 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +namespace jam { + + /// Special zero-size-type for some things + /// (e.g. unsupported, experimental or empty). + struct Empty { + constexpr bool operator==(const Empty &) const = default; + + template + friend inline Stream &operator<<(Stream &s, const Empty &) { + return s; + } + + template + friend inline Stream &operator>>(Stream &s, const Empty &) { + return s; + } + }; + +} // namespace jam diff --git a/src/jam/tagged.hpp b/src/jam/tagged.hpp new file mode 100644 index 0000000..347800e --- /dev/null +++ b/src/jam/tagged.hpp @@ -0,0 +1,89 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#include + +namespace jam { + + template >> + struct Wrapper { + template + Wrapper(Args &&...args) : value(std::forward(args)...) {} + + protected: + T value; + }; + + template , Wrapper, T>> + class Tagged : public Base { + public: + using type = T; + using tag = Tag; + + Tagged() : Base() {} + + Tagged(T value) : Base(std::move(value)) {} + + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) + Tagged &operator=(T &&value) noexcept( + not std::is_lvalue_reference_v) { + if constexpr (std::is_scalar_v) { + this->Wrapper::value = std::forward(value); + } else { + static_cast(*this) = std::forward(value); + } + return *this; + } + + template + explicit operator Out() const { + // NOLINTNEXTLINE(readability-else-after-return) + if constexpr (std::is_scalar_v) { + return this->Wrapper::value; + } else { + return *this; + } + } + + // auto operator<=>(const Tagged &other) const = default; + // bool operator==(const Tagged &other) const = default; + }; + + template < + typename T, + typename Tag, + typename Base = std::conditional_t, Wrapper, T>> + std::ostream &operator<<(std::ostream &os, + const Tagged &view) = delete; +} // namespace jam + +template +::scale::ScaleEncoderStream &operator<<(scale::ScaleEncoderStream &s, + const jam::Tagged &tagged) { + if constexpr (std::is_scalar_v) { + return s << tagged.template Wrapper::value; + } else { + return s << static_cast(tagged); + } +} + +template +::scale::ScaleDecoderStream &operator>>(scale::ScaleDecoderStream &s, + jam::Tagged &tagged) { + if constexpr (std::is_scalar_v) { + return s >> tagged.template Wrapper::value; + } else { + return s >> static_cast(tagged); + } +} diff --git a/src/jam/unused.hpp b/src/jam/unused.hpp new file mode 100644 index 0000000..24f4974 --- /dev/null +++ b/src/jam/unused.hpp @@ -0,0 +1,66 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include + +#include +#include + +namespace jam { + + enum UnusedError : uint8_t { + AttemptToEncodeUnused = 1, + AttemptToDecodeUnused, + }; + Q_ENUM_ERROR_CODE(UnusedError) { + using E = decltype(e); + switch (e) { + case E::AttemptToEncodeUnused: + return "AttemptToEncodeUnused"; + case E::AttemptToDecodeUnused: + return "AttemptToDecodeUnused"; + } + abort(); + } + + /// Number-based marker-type for using as tag + template + struct NumTag { + private: + static constexpr size_t tag = Num; + }; + + /// Special zero-size-type for some things + /// (e.g., dummy types of variant, unsupported or experimental). + template + using Unused = Tagged>; + + template + constexpr bool operator==(const Unused &, const Unused &) { + return true; + } + + /// To raise failure while attempt to encode unused entity + template + inline ::scale::ScaleEncoderStream &operator<<(::scale::ScaleEncoderStream &s, + const Unused &) { + ::scale::raise(UnusedError::AttemptToEncodeUnused); + return s; + } + + /// To raise failure while attempt to decode unused entity + template + inline ::scale::ScaleDecoderStream &operator>>(::scale::ScaleDecoderStream &s, + const Unused &) { + ::scale::raise(UnusedError::AttemptToDecodeUnused); + return s; + } + +} // namespace kagome diff --git a/src/jam/variant_alternative.hpp b/src/jam/variant_alternative.hpp new file mode 100644 index 0000000..d26a333 --- /dev/null +++ b/src/jam/variant_alternative.hpp @@ -0,0 +1,46 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +namespace jam { + + template + struct is_boost_variant : std::false_type {}; + + template + struct is_boost_variant> : std::true_type { + }; + + template + struct variant_alternative_boost { + static_assert(is_boost_variant::value, + "Type is not a boost::variant"); + static_assert(I < std::tuple_size_v, + "Index out of bounds for boost::variant"); + using type = std::tuple_element_t; + }; + + template + struct variant_alternative_std { + static_assert(I < std::variant_size_v, + "Index out of bounds for std::variant"); + using type = std::variant_alternative_t; + }; + + template + struct variant_alternative + : std::conditional_t::value, + variant_alternative_boost, + variant_alternative_std> {}; + + template + using variant_alternative_t = typename variant_alternative::type; + +} // namespace jam diff --git a/test-vectors/.gitignore b/test-vectors/.gitignore new file mode 100644 index 0000000..7069123 --- /dev/null +++ b/test-vectors/.gitignore @@ -0,0 +1,11 @@ +/constants-tiny.hpp +/constants-full.hpp +/config.hpp +/config-tiny.hpp +/config-full.hpp +/common-types.hpp +/common-scale.hpp +/common-diff.hpp +/*/*-types.hpp +/*/*-scale.hpp +/*/*-diff.hpp diff --git a/test-vectors/asn1.cmake b/test-vectors/asn1.cmake index 14dc383..e0a122d 100644 --- a/test-vectors/asn1.cmake +++ b/test-vectors/asn1.cmake @@ -4,47 +4,129 @@ # SPDX-License-Identifier: Apache-2.0 # -function(asn1 name) - set(ASN_DIR ${PROJECT_SOURCE_DIR}/test-vectors/jamtestvectors/${name}) - foreach(x IN LISTS ARGN) - list(APPEND ASN_FILES ${ASN_DIR}/${x}.asn) - endforeach() - set(OUTPUT_PREFIX ${PROJECT_SOURCE_DIR}/test-vectors/${name}/types) - set(OUTPUT ${OUTPUT_PREFIX}.hpp ${OUTPUT_PREFIX}.scale.hpp ${OUTPUT_PREFIX}.diff.hpp) - set(ASN1_PY ${PROJECT_SOURCE_DIR}/python/asn1.py) - add_custom_command( - OUTPUT ${OUTPUT} - COMMAND ${Python3_EXECUTABLE} ${ASN1_PY} ${name} - DEPENDS ${ASN1_PY} ${ASN_FILES} - ) - - add_library(test_vectors_${name}_types INTERFACE ${OUTPUT}) - target_link_libraries(test_vectors_${name}_types INTERFACE - scale::scale - test_vectors_headers - ) - - add_executable(test_vectors_${name}_types_test - types.test.cpp - ) - target_compile_definitions(test_vectors_${name}_types_test PRIVATE PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}") - target_link_libraries(test_vectors_${name}_types_test - fmt::fmt - ${GTEST_DEPS} - test_vectors_${name}_types - ) - add_test(test_vectors_${name}_types_test test_vectors_${name}_types_test) - - add_executable(test_vectors_${name}_test - ${name}.test.cpp - ) - target_compile_definitions(test_vectors_${name}_test PRIVATE PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}") - target_link_libraries(test_vectors_${name}_test - fmt::fmt - ${GTEST_DEPS} - headers - test_vectors_${name}_types - ) - add_test(test_vectors_${name}_test test_vectors_${name}_test) +# Generating constants +if (NOT TARGET generate_constants) + set(ASN_DIR ${PROJECT_SOURCE_DIR}/test-vectors/jamtestvectors) + set(ASN1_PY ${PROJECT_SOURCE_DIR}/python/asn1.py) + file(GLOB ASN_FILES "${ASN_DIR}/jam-types-asn/*-const.asn") + + add_custom_target(generate_constants + COMMENT "Building generated files..." + ) + add_custom_command(TARGET generate_constants + PRE_BUILD + COMMAND ${Python3_EXECUTABLE} ${ASN1_PY} constants + DEPENDS ${ASN1_PY} ${ASN_FILES} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating constants files: ${CMAKE_CURRENT_BINARY_DIR}" + ) +endif () + +# Generating JAM-types +if (NOT TARGET generate_common_types) + set(ASN_DIR ${PROJECT_SOURCE_DIR}/test-vectors/jamtestvectors) + set(ASN1_PY ${PROJECT_SOURCE_DIR}/python/asn1.py) + + file(GLOB ASN_FILES "${ASN_DIR}/jam-types-asn/jam-types.asn") + + add_custom_target(generate_common_types + COMMENT "Building generated files..." + ) + add_dependencies(generate_common_types generate_constants) + add_custom_command(TARGET generate_common_types + PRE_BUILD + COMMAND ${Python3_EXECUTABLE} ${ASN1_PY} types + DEPENDS ${ASN1_PY} ${ASN_FILES} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating types files: ${CMAKE_CURRENT_BINARY_DIR}" + ) +endif () + +# Generating module-dependent types +function(generate_from_asn1 name) + set(TARGET_NAME generate_${name}_types) + + if (TARGET ${TARGET_NAME}) + return() + endif() + + set(ASN_DIR ${PROJECT_SOURCE_DIR}/test-vectors/jamtestvectors) + set(ASN1_PY ${PROJECT_SOURCE_DIR}/python/asn1.py) + + file(GLOB ASN_FILES "${ASN_DIR}/${name}/${name}.asn") + + add_custom_target(${TARGET_NAME} + COMMENT "Building generated files..." + ) + add_dependencies(${TARGET_NAME} generate_common_types) + add_custom_command(TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND ${Python3_EXECUTABLE} ${ASN1_PY} ${name} + DEPENDS ${ASN1_PY} ${ASN_FILES} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating types files: ${CMAKE_CURRENT_BINARY_DIR}" + ) +endfunction() + + +function(add_test_vector name) + set(TEST_VECTOR_${name} 1 PARENT_SCOPE) + generate_from_asn1(${name}) + + set(VECTOR_DIR ${PROJECT_SOURCE_DIR}/test-vectors/${name}) + + file(GLOB HPP_FILES_ "${VECTOR_DIR}/*.hpp") + set(HPP_FILES) + set(REGEX "${name}-(types|scale|diff).*\\.hpp$") + foreach(FILE ${HPP_FILES_}) + string(REGEX MATCH ${REGEX} MATCH_RESULT ${FILE}) + if(MATCH_RESULT) + list(APPEND HPP_FILES ${FILE}) + endif() + endforeach() + + set(TEST_VECTOR test_vector__${name}) + + add_library(${TEST_VECTOR}__types INTERFACE ${HPP_FILES}) + target_link_libraries(${TEST_VECTOR}__types INTERFACE + scale::scale + test_vectors_headers + ) + add_dependencies(${TEST_VECTOR}__types generate_${name}_types) + + add_executable(${TEST_VECTOR}__reencode_test + types.test.cpp + ) + target_compile_definitions(${TEST_VECTOR}__reencode_test PRIVATE PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}") + target_include_directories(${TEST_VECTOR}__reencode_test PUBLIC ${PROJECT_SOURCE_DIR}) + target_link_libraries(${TEST_VECTOR}__reencode_test + fmt::fmt + ${GTEST_DEPS} + ${TEST_VECTOR}__types + ) + add_test(${TEST_VECTOR}__reencode_test ${TEST_VECTOR}__reencode_test) + + add_executable(${TEST_VECTOR}__transition_test + ${name}.test.cpp + ) + target_compile_definitions(${TEST_VECTOR}__transition_test PRIVATE PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}") + target_link_libraries(${TEST_VECTOR}__transition_test + fmt::fmt + ${GTEST_DEPS} + headers + ${TEST_VECTOR}__types + ) + add_test(${TEST_VECTOR}__transition_test ${TEST_VECTOR}__transition_test) +endfunction() + + +function(add_test_vector_libraries name) + if (NOT TEST_VECTOR_${name}) + message(FATAL_ERROR "Call 'add_test_vector(${name})' first") + endif() + + set(TEST_VECTOR test_vector__${name}) + + target_link_libraries(${TEST_VECTOR}__transition_test ${ARGN}) endfunction() diff --git a/test-vectors/config-types-scale.hpp b/test-vectors/config-types-scale.hpp index ca253d6..e7f96b6 100644 --- a/test-vectors/config-types-scale.hpp +++ b/test-vectors/config-types-scale.hpp @@ -6,90 +6,89 @@ #pragma once +#include +#include +#include +#include + #include +#include -#include +#include +#include -namespace scale { - template - void decodeConfig(ScaleDecoderStream &s, T &v, const auto &config) { - s >> v; - } +#include +#include - template - void decodeConfig( - ScaleDecoderStream &s, std::vector &v, const auto &config) { - auto n = s.decodeLength(); - v.resize(0); - v.reserve(n); - for (size_t i = 0; i < n; ++i) { - T item; - decodeConfig(s, item, config); - v.emplace_back(std::move(item)); - } - } +namespace jam { - template - void decodeConfig( - ScaleDecoderStream &s, std::optional &v, const auto &config) { - uint8_t i = 0; - s >> i; - if (i == 0) { - v.reset(); - } else if (i == 1) { - T item; - decodeConfig(s, item, config); - v = std::move(item); - } else { - raise(DecodeError::WRONG_TYPE_INDEX); - } + template + scale::ScaleEncoderStream &operator<<(scale::ScaleEncoderStream &s, + const std::variant &v) { + s << static_cast(v.index()); + std::visit([&](const auto &v) { s << v; }, v); + return s; } template - void decodeConfigVariant( - size_t i, ScaleDecoderStream &s, auto &v, const auto &config) { + void decodeConfigStdVariant(size_t i, scale::ScaleDecoderStream &s, auto &v) { if (i == I) { - T item; - decodeConfig(s, item, config); - v = std::move(item); + v = T{}; + s >> std::get(v); } else if constexpr (sizeof...(Ts) != 0) { - decodeConfigVariant(i, s, v, config); + decodeConfigStdVariant(i, s, v); } } template - void decodeConfig( - ScaleDecoderStream &s, boost::variant &v, const auto &config) { + scale::ScaleDecoderStream &operator>>(scale::ScaleDecoderStream &s, + std::variant &v) { uint8_t i = 0; s >> i; - if (i >= sizeof...(Ts)) { - raise(DecodeError::WRONG_TYPE_INDEX); + if (i < sizeof...(Ts)) { + decodeConfigStdVariant<0, Ts...>(i, s, v); + return s; } - decodeConfigVariant<0, Ts...>(i, s, v, config); + raise(scale::DecodeError::WRONG_TYPE_INDEX); } -} // namespace scale -namespace jam { template - void decodeConfig(scale::ScaleDecoderStream &s, - ConfigVec &v, - const auto &config) { + scale::ScaleEncoderStream &operator<<(scale::ScaleEncoderStream &s, + const ConfigVec &v) { + auto &config = s.getConfig(); auto n = v.configSize(config); - v.v.resize(0); - v.v.reserve(n); - for (size_t i = 0; i < n; ++i) { - T item; - decodeConfig(s, item, config); - v.v.emplace_back(std::move(item)); + assert(v.v.size() == n); + for (auto &item : v.v) { + s << item; } + return s; } template - scale::ScaleEncoderStream &operator<<( - scale::ScaleEncoderStream &s, const ConfigVec &v) { + scale::ScaleDecoderStream &operator>>(scale::ScaleDecoderStream &s, + ConfigVec &v) { + auto &config = s.getConfig(); + auto n = v.configSize(config); + v.v.resize(n); for (auto &item : v.v) { - s << item; + s >> item; } return s; } + + template + outcome::result encode(const T &v, const auto &config) { + scale::ScaleEncoderStream s(config); + OUTCOME_TRY(encode(s, v)); + return outcome::success(s.to_vector()); + } + + template + outcome::result decode(qtils::BytesIn bytes, const auto &config) { + scale::ScaleDecoderStream s(bytes, config); + T t; + OUTCOME_TRY(scale::decode(s, t)); + return outcome::success(std::move(t)); + } + } // namespace jam diff --git a/test-vectors/diff.hpp b/test-vectors/diff.hpp index 2d62e37..a9a713e 100644 --- a/test-vectors/diff.hpp +++ b/test-vectors/diff.hpp @@ -6,8 +6,18 @@ #pragma once +#include +#include +#include +#include +#include +#include + +#include #include +#include +#include #include /** @@ -37,6 +47,7 @@ struct fmt::formatter { #define DIFF_F(...) \ void diff(Indent indent, const __VA_ARGS__ &v1, const __VA_ARGS__ &v2) + #define DIFF_M(m) diff_m(indent, v1.m, v2.m, #m) enum class color { red = 31, green = 32 }; @@ -68,6 +79,13 @@ DIFF_F(uint32_t) { fmt::println("{}{} != {}", indent, v1, v2); } +DIFF_F(jam::Empty) {} + +//template +//DIFF_F(jam::Tagged) { +// diff(indent, (const T &)v1, (const T &)v2); +//} + template DIFF_F(jam::ConfigVec) { diff(indent, v1.v, v2.v); @@ -123,29 +141,60 @@ DIFF_F(qtils::BytesIn) { template requires(std::is_enum_v) -void diff(Indent indent, - const E &v1, - const E &v2, - const std::vector &names) { - fmt::println("{}{} != {}", indent, names[(int)v1], names[(int)v2]); +void diff_e(Indent indent, const E &v1, const E &v2, const auto &names) { + if (v1 == v2) { + return; + } + + auto get_name = [&](auto v) { + using IntType = std::decay_t::key_type; + if (auto it = names.find((IntType)v); it != names.end()) { + return std::string(it->second); + } + return fmt::format("", (IntType)v); + }; + + fmt::println("{}{} != {}", indent, get_name(v1), get_name(v2)); } -template -void diff(Indent indent, - const boost::variant &v1, - const boost::variant &v2, - const std::vector &names) { +template +void diff_v(Indent indent, + const std::variant &v1, + const std::variant &v2, + std::span tags) { if (v1 == v2) { return; } - if (v1.which() != v2.which()) { - fmt::println("{}{} != {}", indent, names[v1.which()], names[v2.which()]); + if (v1.index() != v2.index()) { + fmt::println("{}{} != {}", indent, tags[v1.index()], tags[v2.index()]); + return; + } + + fmt::println("{}{}", indent, tags[v1.index()]); + std::visit( + [&](auto &&v) { + diff(~indent, v, std::get>(v2)); + }, + v1); +} + +template +void diff_v(Indent indent, + const boost::variant &v1, + const boost::variant &v2, + std::span tags) { + if (v1 == v2) { return; } - fmt::println("{}{}", indent, names[v1.which()]); - if (v1.which() == 0) { - diff(~indent, boost::get(v1), boost::get(v2)); - } else { - diff(~indent, boost::get(v1), boost::get(v2)); + if (v1.which() != v2.which()) { + fmt::println("{}{} != {}", indent, tags[v1.which()], tags[v2.which()]); + return; } + + fmt::println("{}{}", indent, tags[v1.which()]); + boost::apply_visitor( + [&](auto &&v) { + diff(~indent, v, boost::get>(v2)); + }, + v1); } diff --git a/test-vectors/disputes/.gitignore b/test-vectors/disputes/.gitignore deleted file mode 100644 index 4fc5df1..0000000 --- a/test-vectors/disputes/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/types.hpp -/types.scale.hpp -/types.diff.hpp diff --git a/test-vectors/disputes/CMakeLists.txt b/test-vectors/disputes/CMakeLists.txt index 193474e..f0a50a6 100644 --- a/test-vectors/disputes/CMakeLists.txt +++ b/test-vectors/disputes/CMakeLists.txt @@ -4,9 +4,11 @@ # SPDX-License-Identifier: Apache-2.0 # -asn1(disputes disputes tiny full) -target_link_libraries(test_vectors_disputes_test +add_test_vector(disputes tiny full) + +add_test_vector_libraries(disputes jam_crust::jam_crust PkgConfig::libb2 schnorrkel_crust::schnorrkel_crust ) + diff --git a/test-vectors/disputes/disputes.hpp b/test-vectors/disputes/disputes.hpp index e0e3ddd..fd6b5c9 100644 --- a/test-vectors/disputes/disputes.hpp +++ b/test-vectors/disputes/disputes.hpp @@ -14,14 +14,17 @@ #include #include #include +#include #include #include +#include +#include #include -#include +#include namespace jam::disputes { - namespace types = test_vectors_disputes; + namespace types = jam::test_vectors; auto asSet(auto &&r) { return std::set(r.begin(), r.end()); @@ -41,7 +44,7 @@ namespace jam::disputes { I begin; I end; - auto makeEnd() const { + [[nodiscard]] auto makeEnd() const { return begin == m.end() ? m.end() : m.upper_bound(begin->first); } It(const M &m, I begin) : m{m}, begin{begin}, end{makeEnd()} {} @@ -60,10 +63,10 @@ namespace jam::disputes { } }; - auto begin() const { + [[nodiscard]] auto begin() const { return It{m, m.begin()}; } - auto end() const { + [[nodiscard]] auto end() const { return It{m, m.end()}; } }; @@ -78,7 +81,7 @@ namespace jam::disputes { qtils::append(payload, X); qtils::append(payload, work_report); return jam::ed25519::verify(sig, payload, pub); - }; + } // [GP 0.4.5 I.4.5] // $jam_valid - Ed25519 Judgments for valid work-reports. @@ -96,13 +99,13 @@ namespace jam::disputes { 'j', 'a', 'm', '_', 'g', 'u', 'a', 'r', 'a', 'n', 't', 'e', 'e'}; /// Given state and input, derive next state and output. - inline std::pair transition( + inline std::pair transition( const types::Config &config, - const types::State &state, - const types::Input &input) { - using Error = types::ErrorCode; + const types::disputes::State &state, + const types::disputes::Input &input) { + using Error = types::disputes::ErrorCode; const auto error = [&](Error error) { - return std::make_pair(state, types::Output{error}); + return std::make_pair(state, types::disputes::Output{error}); }; // [GP 0.4.5 10.1] @@ -113,17 +116,17 @@ namespace jam::disputes { // [GP 0.4.5 10 97] // ψg - set of work-reports which were judged as correct - const auto &good_set = dispute_records.psi_g; + const auto &good_set = dispute_records.good; // ψb - set of work-reports which were judged as incorrect - const auto &bad_set = dispute_records.psi_b; + const auto &bad_set = dispute_records.bad; // ψw - set of work-reports which were appeared impossible to judge - const auto &wonky_set = dispute_records.psi_w; + const auto &wonky_set = dispute_records.wonky; // ψo - a set of Ed25519 keys representing validators which were found to // have misjudged a work-report - const auto &punish_set = dispute_records.psi_o; + const auto &punish_set = dispute_records.offenders; // [GP 0.4.5 10.2] // ED ≡ (v, c, f) @@ -146,12 +149,12 @@ namespace jam::disputes { // Signature related judgment offenders. const auto &faults = ext.faults; - types::EpochIndex current_epoch = state.tau / config.epoch_length; + auto current_epoch = state.tau / config.epoch_length; // к - kappa, aka validator set of current epoch const auto ¤t_epoch_validator_set = state.kappa; - types::EpochIndex previous_epoch = + auto previous_epoch = current_epoch ? current_epoch - 1 : 0; // For using copy of epoch 0 as of previous one @@ -159,11 +162,10 @@ namespace jam::disputes { const auto &previous_epoch_validator_set = state.lambda; // Verdicts for registration - std::vector verdicts_registry; - std::unordered_multimap< - types::WorkReportHash, - std::reference_wrapper, - qtils::BytesStdHash> + std::vector verdicts_registry; + std::unordered_multimap, + qtils::BytesStdHash> judgements_registry; // Check verdicts. @@ -246,7 +248,7 @@ namespace jam::disputes { } std::multimap, + std::reference_wrapper, std::less<> // std::equal_to<> // hash_range @@ -255,7 +257,7 @@ namespace jam::disputes { // Check culprits { - std::optional prev_validator_key{}; + std::optional prev_validator_key{}; for (const auto &culprit : culprits) { const auto &work_report = culprit.target; const auto &validator_key = culprit.key; @@ -305,7 +307,7 @@ namespace jam::disputes { } std::multimap, + std::reference_wrapper, std::less<> // hash_range > @@ -313,7 +315,7 @@ namespace jam::disputes { // Check faults { - std::optional prev_validator_key{}; + std::optional prev_validator_key{}; for (const auto &fault : faults) { const auto &work_report = fault.target; const auto vote = fault.vote; @@ -416,7 +418,7 @@ namespace jam::disputes { // The offenders markers must contain exactly the keys of all new offenders, // respectively // [GP 0.4.5 10.2 (116)] - std::vector offenders_mark; + std::vector offenders_mark; // Analise verdicts for (const auto &[work_report, counts] : vote_count_by_judgements) { @@ -463,9 +465,9 @@ namespace jam::disputes { } // Analise culprits - for (auto [work_report, culprits] : MultimapGroups{culprits_registry}) { + for (auto [work_report, culprits_] : MultimapGroups{culprits_registry}) { // Check if the verdict of culprit is bad - for (auto &culprit : culprits) { + for (auto &culprit : culprits_) { const auto &validator_public = culprit.get().key; offenders_mark.emplace_back(validator_public); // [GP 0.4.5 10.2 (116)] new_punish_set.emplace(validator_public); // [GP 0.4.5 10.2 (115)] @@ -474,10 +476,10 @@ namespace jam::disputes { } // Analise faults - for (auto [work_report, faults] : MultimapGroups{faults_registry}) { + for (auto [work_report, faults_] : MultimapGroups{faults_registry}) { // Check if the verdict of fault is bad - for (auto &fault : faults) { - if (fault.get().vote != false) { // voting opposite + for (auto &fault : faults_) { + if (fault.get().vote) { // voting opposite return error(Error::fault_verdict_wrong); } const auto &validator_public = fault.get().key; @@ -494,25 +496,27 @@ namespace jam::disputes { // their core // [GP 0.4.5 10.2 (111)] for (auto &row_work_report : work_reports.v) { - auto work_report = mathcal_H(row_work_report->dummy_work_report); - if (new_bad_set.contains(work_report) - or new_wonky_set.contains(work_report)) { - row_work_report.reset(); + if (row_work_report.has_value()) { + auto work_report = + mathcal_H(jam::encode(row_work_report.value().report, config).value()); + if (new_bad_set.contains(work_report) + or new_wonky_set.contains(work_report)) { + row_work_report.reset(); + } } } auto state_tick = state; - state_tick.psi.psi_g = asVec(new_good_set); - state_tick.psi.psi_b = asVec(new_bad_set); - state_tick.psi.psi_w = asVec(new_wonky_set); - state_tick.psi.psi_o = asVec(new_punish_set); + state_tick.psi.good = asVec(new_good_set); + state_tick.psi.bad = asVec(new_bad_set); + state_tick.psi.wonky = asVec(new_wonky_set); + state_tick.psi.offenders = asVec(new_punish_set); state_tick.rho = work_reports; return { state_tick, - types::Output{types::DisputesOutputMarks{ + types::disputes::Output{types::disputes::OutputData{ .offenders_mark = {offenders_mark.begin(), offenders_mark.end()}, - }}, - }; + }}}; } } // namespace jam::disputes diff --git a/test-vectors/disputes/disputes.test.cpp b/test-vectors/disputes/disputes.test.cpp index 66ce5a8..2989578 100644 --- a/test-vectors/disputes/disputes.test.cpp +++ b/test-vectors/disputes/disputes.test.cpp @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include -#include #include -GTEST_VECTORS(Disputes, jam::test_vectors_disputes::Vectors); +GTEST_VECTORS(Disputes, disputes); -GTEST_VECTORS_TEST_TRANSITION(Disputes, jam::disputes); +GTEST_VECTORS_TEST_TRANSITION(Disputes, disputes); diff --git a/test-vectors/disputes/types.test.cpp b/test-vectors/disputes/types.test.cpp index c872451..b84fa57 100644 --- a/test-vectors/disputes/types.test.cpp +++ b/test-vectors/disputes/types.test.cpp @@ -6,9 +6,9 @@ #include -GTEST_VECTORS(Disputes, jam::test_vectors_disputes::Vectors); +GTEST_VECTORS(Disputes, disputes); /** * Check python generated scale encoding/decoding against test vectors. */ -GTEST_VECTORS_TEST_REENCODE(Disputes); +GTEST_VECTORS_TEST_REENCODE(Disputes, disputes); diff --git a/test-vectors/disputes/vectors.hpp b/test-vectors/disputes/vectors.hpp index 83d57f8..08c9a07 100644 --- a/test-vectors/disputes/vectors.hpp +++ b/test-vectors/disputes/vectors.hpp @@ -6,15 +6,17 @@ #pragma once -#include +#include +#include +#include #include -namespace jam::test_vectors_disputes { +namespace jam::test_vectors::disputes { struct Vectors : test_vectors::VectorsT { std::string_view type; explicit Vectors(bool is_full) - : VectorsT{is_full ? config_full : config_tiny}, + : VectorsT{is_full ? config::full : config::tiny}, type{is_full ? "full" : "tiny"} { this->list(std::filesystem::path{"disputes"} / type); } @@ -26,4 +28,4 @@ namespace jam::test_vectors_disputes { }; } }; -} // namespace jam::test_vectors_disputes +} // namespace jam::test_vectors::disputes diff --git a/test-vectors/history/.gitignore b/test-vectors/history/.gitignore deleted file mode 100644 index 4fc5df1..0000000 --- a/test-vectors/history/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/types.hpp -/types.scale.hpp -/types.diff.hpp diff --git a/test-vectors/history/CMakeLists.txt b/test-vectors/history/CMakeLists.txt index 87dbe28..f149342 100644 --- a/test-vectors/history/CMakeLists.txt +++ b/test-vectors/history/CMakeLists.txt @@ -4,4 +4,6 @@ # SPDX-License-Identifier: Apache-2.0 # -asn1(history history) +add_test_vector(history) + + diff --git a/test-vectors/history/history.hpp b/test-vectors/history/history.hpp index acdb08e..091cf93 100644 --- a/test-vectors/history/history.hpp +++ b/test-vectors/history/history.hpp @@ -9,10 +9,13 @@ #include #include -#include +#include +#include +#include +#include namespace jam::history { - namespace types = test_vectors_history; + namespace types = jam::test_vectors; /** * The size of recent history, in blocks @@ -23,7 +26,7 @@ namespace jam::history { // [GP 0.4.5 E.2 333] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/merklization.tex#L205 - inline types::Mmr mathcal_A(types::Mmr r, types::Hash l) { + inline types::Mmr mathcal_A(types::Mmr r, types::OpaqueHash l) { for (size_t n = 0; n < r.peaks.size(); ++n) { if (not r.peaks[n]) { r.peaks[n] = l; @@ -39,10 +42,10 @@ namespace jam::history { /** * Given state and input, derive next state and output. */ - inline std::pair transition( + inline std::pair transition( const types::Config & /*config*/, - const types::State &state, - const types::Input &input) { + const types::history::State &state, + const types::history::Input &input) { // [GP 0.4.5 7 84] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/recent_history.tex#L32 std::vector beta_tick(state.beta.size() >= H ? std::next(state.beta.begin()) @@ -63,9 +66,9 @@ namespace jam::history { beta_tick.emplace_back(types::BlockInfo{ .header_hash = input.header_hash, .mmr = mmr_tick, - .state_root = types::Hash{}, + .state_root = types::StateRoot{}, .reported = input.work_packages, }); - return std::make_pair(types::State{.beta = beta_tick}, types::Output{}); + return std::make_pair(types::history::State{.beta = beta_tick}, types::history::Output{}); } } // namespace jam::history diff --git a/test-vectors/history/history.test.cpp b/test-vectors/history/history.test.cpp index 584e32b..5071e96 100644 --- a/test-vectors/history/history.test.cpp +++ b/test-vectors/history/history.test.cpp @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include -#include #include -GTEST_VECTORS(History, jam::test_vectors_history::Vectors); +GTEST_VECTORS(History, history); -GTEST_VECTORS_TEST_TRANSITION(History, jam::history); +GTEST_VECTORS_TEST_TRANSITION(History, history); diff --git a/test-vectors/history/types.test.cpp b/test-vectors/history/types.test.cpp index 9efdb63..7eb65ed 100644 --- a/test-vectors/history/types.test.cpp +++ b/test-vectors/history/types.test.cpp @@ -5,10 +5,11 @@ */ #include +#include -GTEST_VECTORS(History, jam::test_vectors_history::Vectors); +GTEST_VECTORS(History, history); /** * Check python generated scale encoding/decoding against test vectors. */ -GTEST_VECTORS_TEST_REENCODE(History); +GTEST_VECTORS_TEST_REENCODE(History, history); \ No newline at end of file diff --git a/test-vectors/history/vectors.hpp b/test-vectors/history/vectors.hpp index 8caef0b..83eee63 100644 --- a/test-vectors/history/vectors.hpp +++ b/test-vectors/history/vectors.hpp @@ -6,13 +6,15 @@ #pragma once -#include +#include +#include +#include #include -namespace jam::test_vectors_history { +namespace jam::test_vectors::history { struct Vectors : test_vectors::VectorsT { Vectors() : VectorsT{Config{}} { - this->list("history/data"); + this->list(std::filesystem::path{"history/data"}); } static std::vector> vectors() { diff --git a/test-vectors/jamtestvectors b/test-vectors/jamtestvectors index b9ba221..2e0c424 160000 --- a/test-vectors/jamtestvectors +++ b/test-vectors/jamtestvectors @@ -1 +1 @@ -Subproject commit b9ba221f4798ba8ebbfe64779e3d44ec1a35fe00 +Subproject commit 2e0c424d5171c463191625d1c9f9327e9b084e4b diff --git a/test-vectors/safrole/.gitignore b/test-vectors/safrole/.gitignore deleted file mode 100644 index 4fc5df1..0000000 --- a/test-vectors/safrole/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/types.hpp -/types.scale.hpp -/types.diff.hpp diff --git a/test-vectors/safrole/CMakeLists.txt b/test-vectors/safrole/CMakeLists.txt index ea94dcf..ce84d00 100644 --- a/test-vectors/safrole/CMakeLists.txt +++ b/test-vectors/safrole/CMakeLists.txt @@ -4,8 +4,9 @@ # SPDX-License-Identifier: Apache-2.0 # -asn1(safrole safrole tiny full) -target_link_libraries(test_vectors_safrole_test - jam_crust::jam_crust - PkgConfig::libb2 +add_test_vector(safrole tiny full) + +add_test_vector_libraries(safrole + jam_crust::jam_crust + PkgConfig::libb2 ) diff --git a/test-vectors/safrole/safrole.hpp b/test-vectors/safrole/safrole.hpp index 9d37834..3f8191d 100644 --- a/test-vectors/safrole/safrole.hpp +++ b/test-vectors/safrole/safrole.hpp @@ -8,14 +8,21 @@ #include #include +#include #include +#include #include +#include +#include +#include +#include #include -#include +#include namespace jam::safrole { - namespace types = test_vectors_safrole; + namespace types = jam::test_vectors; + using BandersnatchSignature = decltype(types::TicketEnvelope::signature); // [GP 0.4.5 G 339] @@ -28,14 +35,14 @@ namespace jam::safrole { } struct TicketBodyLess { - bool operator()( - const types::TicketBody &l, const types::TicketBody &r) const { + bool operator()(const types::TicketBody &l, + const types::TicketBody &r) const { return l.id < r.id; } }; - using GammaA = decltype(types::State::gamma_a); - using GammaZ = decltype(types::State::gamma_z); + using GammaA = decltype(types::safrole::State::gamma_a); + using GammaZ = decltype(types::safrole::State::gamma_z); using BandersnatchKeys = decltype(types::EpochMark::validators); inline auto &ring_ctx(const types::Config &config) { @@ -54,11 +61,6 @@ namespace jam::safrole { constexpr qtils::BytesN<15> X_T = {'j','a','m','_','t','i','c','k','e','t','_','s','e','a','l'}; // clang-format on - // The number of ticket entries per validator. - // [GP 0.4.5 I.4.4] - // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/definitions.tex#L271 - constexpr uint32_t N = 2; - // The maximum number of tickets which may be submitted in a single extrinsic. // [GP 0.4.5 I.4.4] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/definitions.tex#L269 @@ -75,8 +77,8 @@ namespace jam::safrole { // [GP 0.4.5 G 340] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/bandersnatch.tex#L15 - inline GammaZ mathcal_O( - const types::Config &config, const BandersnatchKeys &pks) { + inline GammaZ mathcal_O(const types::Config &config, + const BandersnatchKeys &pks) { return ring_ctx(config).commitment(pks.v).value(); } @@ -100,29 +102,63 @@ namespace jam::safrole { /** * Given state and input, derive next state and output. */ - inline std::pair transition( + inline std::pair transition( const types::Config &config, - const types::State &state, - const types::Input &input) { + const types::safrole::State &state, + const types::safrole::Input &input) { /// The length of an epoch in timeslots. // [GP 0.4.5 I.4.4] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/definitions.tex#L260 const auto E = config.epoch_length; + // The number of ticket entries per validator. + // [GP 0.4.5 I.4.4] + // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/definitions.tex#L271 + const uint32_t N = config.tickets_per_validator; + /// The number of slots into an epoch at which ticket-submission ends. // [GP 0.4.5 I.4.4] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/definitions.tex#L287 const auto Y = E * 5 / 6; - /// H_t - a time-slot index - const auto &[H_t, banderout_H_v, E_T, offenders_tick] = input; - const auto - &[tau, eta, lambda, kappa, gamma_k, iota, gamma_a, gamma_s, gamma_z] = - state; + const auto &[ + // [H_t] Current time slot as found within the block header. + slot, + // [Y(H_v)] Per block entropy generated using per block entropy source. + entropy, + // [E_T] Tickets extrinsic. + extrinsic] = input; + + const auto &[ + // [τ] Prior most recent block's timeslot. Mutated to τ'. + tau, + // [η] Prior entropy buffer. Mutated to η'. + eta, + // [λ] Prior previous epoch validator keys and metadata. Mutated to λ'. + lambda, + // [κ] Prior current epoch validator keys and metadata. Mutated to κ'. + kappa, + // [γ_k] Prior next epoch validator keys and metadata. Mutated to γ'_k. + gamma_k, + // [ι] Prior scheduled validator keys and metadata. Mutated to ι'. + iota, + // [γ_a] Prior sealing-key contest ticket accumulator. Mutated to γ'_a. + gamma_a, + // [γ_s] Prior sealing-key series of the current epoch. Mutated to γ'_s. + gamma_s, + // [γ_z] Prior Bandersnatch ring commitment. Mutated to γ'_z. + gamma_z, + // [ψ'_o] Posterior offenders sequence. + post_offenders] = state; + + // η0 defines the state of the randomness accumulator to which the provably + // random output of the vrf, the signature over some unbiasable input, is + // combined each block. η1, η2 and η3 meanwhile retain the state of this + // accumulator at the end of the three most recently ended epochs in order. const auto &[eta_0, eta_1, eta_2, eta_3] = eta; - using Error = types::CustomErrorCode; + using Error = types::safrole::ErrorCode; const auto error = [&](Error error) { - return std::make_pair(state, types::Output{error}); + return std::make_pair(state, types::safrole::Output{error}); }; // A block may only be regarded as valid once the time-slot index Ht is in @@ -130,7 +166,7 @@ namespace jam::safrole { // It's always strictly greater than that of its parent. // [GP 0.4.5 5 42] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/header.tex#L29 - if (H_t <= state.tau) { + if (slot <= state.tau) { return error(Error::bad_slot); } @@ -138,7 +174,7 @@ namespace jam::safrole { // as defined in the block’s header. // [GP 0.4.5 6.1 46] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L25 - const auto tau_tick = H_t; + const auto tau_tick = slot; // [GP 0.4.5 6.1 47] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L30 @@ -156,11 +192,11 @@ namespace jam::safrole { // [GP 0.4.5 6.7 75] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L251 - if (E_T.size() > K) { + if (extrinsic.size() > K) { throw std::logic_error("not covered by test vectors"); } - if (m_tick >= Y and not E_T.empty()) { - return std::make_pair(state, types::Output{Error::unexpected_ticket}); + if (m_tick >= Y and not extrinsic.empty()) { + return error(Error::unexpected_ticket); } // [GP 0.4.5 6.3 59] @@ -169,8 +205,7 @@ namespace jam::safrole { types::ValidatorsData k_tick; for (auto &validator : k.v) { k_tick.v.emplace_back( - std::ranges::find(offenders_tick, validator.ed25519) - != offenders_tick.end() + qtils::cxx23::ranges::contains(post_offenders, validator.ed25519) ? types::ValidatorData{} : validator); } @@ -191,64 +226,93 @@ namespace jam::safrole { // [GP 0.4.5 6.4 67] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L147 - const auto eta_tick_0 = mathcal_H(frown(eta_0, banderout_H_v)); + // η0 defines the state of the randomness accumulator to which the provably + // random output of the vrf, the signature over some unbiasable input, is + // combined each block. η1, η2 and η3 meanwhile retain the state of this + // accumulator at the end of the three most recently ended epochs in order. + const auto eta_tick_0 = mathcal_H(frown(eta_0, entropy)); // [GP 0.4.5 6.4 68] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L152 + // On an epoch transition (identified as the condition e′ > e), we therefore + // rotate the accumulator value into the history η1, η2 and η3: const auto [eta_tick_1, eta_tick_2, eta_tick_3] = change_epoch ? std::tuple{eta_0, eta_1, eta_2} : std::tuple{eta_1, eta_2, eta_3}; - std::vector n; - for (auto &[r, p] : E_T) { - // [GP 0.4.5 6.7 74] - // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L250 - if (r >= N) { - return error(Error::bad_ticket_attempt); - } - const auto m = frown(X_T, doubleplus(eta_tick_2, r)); - const auto y = bandersnatch(config, gamma_z, m, p); - if (not y) { + // The new ticket accumulator γ′ a is constructed by + // merging new tickets into the previous accumulator value + // (or the empty sequence if it is a new epoch) + // [GP 0.5.2 6.7 (6.34)] + std::set gamma_tick_a; + if (not change_epoch) { + gamma_tick_a.insert(gamma_a.begin(), gamma_a.end()); + } + + std::optional prev_ticket; + for (const auto &ticket_envelope : extrinsic) { + auto &[attempt, ticket_proof] = ticket_envelope; + + auto m = frown(X_T, doubleplus(eta_tick_2, attempt)); + const auto ticket_id_opt = bandersnatch(config, gamma_z, m, ticket_proof); + if (not ticket_id_opt.has_value()) { return error(Error::bad_ticket_proof); } - // [GP 0.4.5 6.7 76] - // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L259 - n.emplace_back(types::TicketBody{*y, r}); - } - // [GP 0.4.5 6.7 77] - // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L264 - if (not std::is_sorted(n.begin(), n.end(), TicketBodyLess{})) { - return error(Error::bad_ticket_order); - } + const auto &ticket_id = ticket_id_opt.value(); - // [GP 0.4.5 6.7 79] - // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L271 - std::vector gamma_tick_a; - if (change_epoch) { - gamma_tick_a = n; - } else { - std::ranges::set_union( - n, gamma_a, std::back_inserter(gamma_tick_a), TicketBodyLess{}); - // [GP 0.4.5 6.7 78] - // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L265 - if (gamma_tick_a.size() != gamma_a.size() + n.size()) { + types::TicketBody ticket{ + .id = ticket_id, + .attempt = attempt, + }; + + fmt::println("new: {}-{}", ticket.id[0], ticket.attempt); + + // We define the extrinsic as a sequence of proofs of valid tickets, + // each of which is a tuple of an entry index (a natural number less + // than N) and a proof of ticket validity + // [GP 0.5.2 6.7 (6.29)] + if (attempt >= N) { + return error(Error::bad_ticket_attempt); + } + + // Duplicate identifiers are neve allowed lest a validator submit the + // same ticket multiple times + // [GP 0.5.2 6.7 (6.33)] + if (qtils::cxx23::ranges::contains(gamma_tick_a, ticket)) { return error(Error::duplicate_ticket); } - } - if (gamma_tick_a.size() > E) { - gamma_tick_a.resize(E); + + // The tickets submitted via the extrinsic must already have been placed + // in order of their implied identifier. + // [GP 0.5.2 6.7 (6.32)] + if (prev_ticket and TicketBodyLess{}(ticket, *prev_ticket)) { + return error(Error::bad_ticket_order); + } + prev_ticket = ticket; + + // [GP 0.5.2 6.7 (6.34)] + gamma_tick_a.emplace(ticket); + + // The maximum size of the ticket accumulator is E. On each block, the + // accumulator becomes the lowest items of the sorted union of tickets + // from prior accumulator γa and the submitted tickets. + if (gamma_tick_a.size() > E) { + gamma_tick_a.erase(std::prev(gamma_tick_a.end())); + } } // [GP 0.4.5 6.5 70] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L182 const auto Z = [&](const GammaA &s) { - types::TicketsBodies tickets; + using TicketsBodies = + variant_alternative_t<0, types::TicketsOrKeys::type>; + TicketsBodies tickets; if (s.size() != E) { throw std::logic_error{"Z"}; } auto it1 = s.begin(), it2 = std::prev(s.end()); auto odd = true; - for (uint32_t i = 0; i < tickets.configSize(config); ++i) { + for (uint32_t i = 0; i < TicketsBodies::configSize(config); ++i) { tickets.v.emplace_back(odd ? *it1 : *it2); if (odd) { ++it1; @@ -259,15 +323,18 @@ namespace jam::safrole { } return tickets; }; + // [GP 0.4.5 6.5 71] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L190 const auto F = [&](const types::OpaqueHash &r, const types::ValidatorsData &k) { - types::EpochKeys keys; + using EpochKeys = variant_alternative_t<1, types::TicketsOrKeys::type>; + EpochKeys keys; for (uint32_t i = 0; i < E; ++i) { - keys.v.emplace_back(circlearrowleft( - k.v, de(first_bytes<4>(mathcal_H(frown(r, mathcal_E<4>(i)))))) - .bandersnatch); + keys.v.emplace_back( + circlearrowleft( + k.v, de(first_bytes<4>(mathcal_H(frown(r, mathcal_E<4>(i)))))) + .bandersnatch); } return keys; }; @@ -279,9 +346,21 @@ namespace jam::safrole { : e_tick == e ? gamma_s : types::TicketsOrKeys{F(eta_tick_2, kappa_tick)}; - types::Output output; + // the header’s epoch marker He is either empty or, if the block is the + // first in a new epoch, then a tuple of the next and current epoch + // randomness, along with a sequence of Bandersnatch keys defining the + // Bandersnatch validator keys (kb) beginning in the next epoch. + // [GP 0.5.2 6.6 (6.27)] + std::optional epoch_mark{}; + if (change_epoch) { + epoch_mark = + types::EpochMark{.entropy = eta_tick_1, + .tickets_entropy = eta_tick_2, + .validators = bandersnatch_keys(gamma_tick_k)}; + }; + return { - types::State{ + types::safrole::State{ .tau = tau_tick, .eta = {eta_tick_0, eta_tick_1, eta_tick_2, eta_tick_3}, .lambda = lambda_tick, @@ -289,16 +368,13 @@ namespace jam::safrole { .gamma_k = gamma_tick_k, // TODO(turuslan): #3, wait for test vectors .iota = iota, - .gamma_a = gamma_tick_a, + .gamma_a = {gamma_tick_a.begin(), gamma_tick_a.end()}, .gamma_s = gamma_tick_s, .gamma_z = gamma_tick_z, + .post_offenders = post_offenders, }, - types::Output{types::OutputMarks{ - // [GP 0.4.5 6.6 72] - // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L216 - .epoch_mark = change_epoch ? std::make_optional(types::EpochMark{ - eta_tick_1, bandersnatch_keys(gamma_tick_k)}) - : std::nullopt, + types::safrole::Output{types::safrole::OutputData{ + .epoch_mark = epoch_mark, // [GP 0.4.5 6.6 73] // https://github.com/gavofyork/graypaper/blob/v0.4.5/text/safrole.tex#L224 .tickets_mark = diff --git a/test-vectors/safrole/safrole.test.cpp b/test-vectors/safrole/safrole.test.cpp index 679506f..436afb8 100644 --- a/test-vectors/safrole/safrole.test.cpp +++ b/test-vectors/safrole/safrole.test.cpp @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include -#include #include -GTEST_VECTORS(Safrole, jam::test_vectors_safrole::Vectors); +GTEST_VECTORS(Safrole, safrole); -GTEST_VECTORS_TEST_TRANSITION(Safrole, jam::safrole); +GTEST_VECTORS_TEST_TRANSITION(Safrole, safrole); diff --git a/test-vectors/safrole/types.test.cpp b/test-vectors/safrole/types.test.cpp index a42502a..a9a1efd 100644 --- a/test-vectors/safrole/types.test.cpp +++ b/test-vectors/safrole/types.test.cpp @@ -6,9 +6,9 @@ #include -GTEST_VECTORS(Safrole, jam::test_vectors_safrole::Vectors); +GTEST_VECTORS(Safrole, safrole); /** * Check python generated scale encoding/decoding against test vectors. */ -GTEST_VECTORS_TEST_REENCODE(Safrole); +GTEST_VECTORS_TEST_REENCODE(Safrole, safrole); diff --git a/test-vectors/safrole/vectors.hpp b/test-vectors/safrole/vectors.hpp index b30d549..b7e36ef 100644 --- a/test-vectors/safrole/vectors.hpp +++ b/test-vectors/safrole/vectors.hpp @@ -6,15 +6,17 @@ #pragma once -#include +#include +#include +#include #include -namespace jam::test_vectors_safrole { +namespace jam::test_vectors::safrole { struct Vectors : test_vectors::VectorsT { std::string_view type; explicit Vectors(bool is_full) - : VectorsT{is_full ? config_full : config_tiny}, + : VectorsT{is_full ? config::full : config::tiny}, type{is_full ? "full" : "tiny"} { this->list(std::filesystem::path{"safrole"} / type); } @@ -26,4 +28,4 @@ namespace jam::test_vectors_safrole { }; } }; -} // namespace jam::test_vectors_safrole +} // namespace jam::test_vectors::safrole diff --git a/test-vectors/vectors.hpp b/test-vectors/vectors.hpp index 5d19989..dc840e4 100644 --- a/test-vectors/vectors.hpp +++ b/test-vectors/vectors.hpp @@ -13,16 +13,20 @@ #include #include #include +#include +#include /** * Common functions for test vectors */ -#define GTEST_VECTORS(VectorName, T) \ - struct VectorName##Test : jam::test_vectors::TestT {}; \ +#define GTEST_VECTORS(VectorName, NsPart) \ + struct VectorName##Test \ + : jam::test_vectors::TestT {}; \ INSTANTIATE_TEST_SUITE_P( \ VectorName, \ VectorName##Test, \ testing::ValuesIn([] { \ + using T = jam::test_vectors::NsPart::Vectors; \ std::vector, std::filesystem::path>> \ params; \ for (auto &vectors : T::vectors()) { \ @@ -42,24 +46,30 @@ * @when transition with `input` * @then get expected `post_state` and `output` */ -#define GTEST_VECTORS_TEST_TRANSITION(VectorName, Namespace) \ - using jam::test_vectors::getTestLabel; \ - TEST_P(VectorName##Test, Transition) { \ - fmt::println("Test transition for '{}'\n", getTestLabel(path)); \ - auto testcase = vectors.read(path); \ - auto [state, output] = Namespace::transition( \ - vectors.config, testcase.pre_state, testcase.input); \ - Indent indent{1}; \ - EXPECT_EQ(state, testcase.post_state) \ - << "Actual and expected states are differ"; \ - if (state != testcase.post_state) { \ - diff_m(indent, state, testcase.post_state, "state"); \ - } \ - EXPECT_EQ(output, testcase.output) \ - << "Actual and expected outputs are differ"; \ - if (output != testcase.output) { \ - diff_m(indent, output, testcase.output, "output"); \ - } \ +#define GTEST_VECTORS_TEST_TRANSITION(VectorName, NsPart) \ + TEST_P(VectorName##Test, Transition) { \ + using jam::test_vectors::getTestLabel; \ + fmt::println("Test transition for '{}'\n", getTestLabel(path)); \ + \ + ASSERT_OUTCOME_SUCCESS(raw_data, qtils::readBytes(path)); \ + \ + ASSERT_OUTCOME_SUCCESS(testcase, \ + jam::decode( \ + raw_data, vectors.config)); \ + \ + auto [state, output] = jam::NsPart::transition( \ + vectors.config, testcase.pre_state, testcase.input); \ + Indent indent{1}; \ + EXPECT_EQ(state, testcase.post_state) \ + << "Actual and expected states are differ"; \ + if (state != testcase.post_state) { \ + diff_m(indent, state, testcase.post_state, "state"); \ + } \ + EXPECT_EQ(output, testcase.output) \ + << "Actual and expected outputs are differ"; \ + if (output != testcase.output) { \ + diff_m(indent, output, testcase.output, "output"); \ + } \ } /** @@ -68,14 +78,21 @@ * @when decode it and encode back * @then `actual` result has the same value as `original` */ -#define GTEST_VECTORS_TEST_REENCODE(VectorName) \ - using jam::test_vectors::getTestLabel; \ - TEST_P(VectorName##Test, Reencode) { \ - fmt::println("Test reencode for '{}'\n", getTestLabel(path)); \ - auto expected = vectors.readRaw(path); \ - auto decoded = vectors.decode(expected); \ - auto reencoded = scale::encode(decoded).value(); \ - EXPECT_EQ(reencoded, expected); \ +#define GTEST_VECTORS_TEST_REENCODE(VectorName, NsPart) \ + TEST_P(VectorName##Test, Reencode) { \ + using jam::test_vectors::getTestLabel; \ + fmt::println("Test reencode for '{}'\n", getTestLabel(path)); \ + \ + ASSERT_OUTCOME_SUCCESS(raw_data, qtils::readBytes(path)); \ + const auto &original = raw_data; \ + \ + ASSERT_OUTCOME_SUCCESS(decoded, \ + jam::decode( \ + original, vectors.config)); \ + \ + ASSERT_OUTCOME_SUCCESS(reencoded, jam::encode(decoded, vectors.config)); \ + \ + EXPECT_EQ(reencoded, original); \ } namespace jam::test_vectors { @@ -121,12 +138,12 @@ namespace jam::test_vectors { VectorsT(Config config) : config{config} {} void list(const std::filesystem::path &relative) { - auto ext_scale = ".scale", ext_json = ".json"; - auto use_ext = ext_scale; + auto ext_bin = ".bin", ext_json = ".json", ext_scale = ".scale"; + auto use_ext = ext_bin; std::map path_ok; for (auto &file : std::filesystem::directory_iterator{dir / relative}) { auto path = file.path(), ext = path.extension(); - if (ext != ext_scale and ext != ext_json) { + if (ext != ext_bin and ext != ext_json and ext != ext_scale) { continue; } path.replace_extension(use_ext); @@ -136,8 +153,8 @@ namespace jam::test_vectors { for (auto &[path, ok] : path_ok) { if (not ok) { fmt::println( - "{}:{} warning: {} is missing, but files with other extensions " - "are available", + "{}:{} warning: {} is missing, " + "but files with other extensions are available", __FILE__, __LINE__, path.native()); @@ -147,20 +164,17 @@ namespace jam::test_vectors { } } - auto decode(qtils::BytesIn raw) const { - scale::ScaleDecoderStream s{raw}; - T testcase; - decodeConfig(s, testcase, config); - return testcase; - } - - static auto readRaw(const std::filesystem::path &path) { - return qtils::readBytes(path).value(); - } - - auto read(const std::filesystem::path &path) const { - return decode(readRaw(path)); - } + // auto decode(qtils::BytesIn raw) const { + // return jam::decode(raw, config); + // } + // + // auto encode(const auto &value) const { + // return jam::encode(value, config); + // } + // + // static auto readRaw(const std::filesystem::path &path) { + // return qtils::readBytes(path); + // } }; template diff --git a/vcpkg-overlay/qtils/portfile.cmake b/vcpkg-overlay/qtils/portfile.cmake index 9f91a98..6f6bb8d 100644 --- a/vcpkg-overlay/qtils/portfile.cmake +++ b/vcpkg-overlay/qtils/portfile.cmake @@ -2,8 +2,8 @@ vcpkg_check_linkage(ONLY_STATIC_LIBRARY) vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO qdrvm/qtils - REF 665971b1774b0b3e6b0bdba864480e85a5c7d1d5 - SHA512 23f443e48c66d8c3a20c419597c24717ef330f967b81ba3c7ad90575c53a6e321cd7d5ecd629ce772b1852f72c43a0b2e3d4a0ab1f7776a8ec9c7294b82b3c3d + REF 0dd35029f54e9a6218ee261b0227ef23762f2d4b + SHA512 fa749d2d489282073e10ef43487a8a98733ec071ebfebe665c8c9692568a15b5542689e4f23aeef5c1442ba6513d8c9563a22169bf0657a619ad01109a84ce0b ) vcpkg_cmake_configure(SOURCE_PATH "${SOURCE_PATH}") vcpkg_cmake_install() diff --git a/vcpkg-overlay/scale/portfile.cmake b/vcpkg-overlay/scale/portfile.cmake index 4df6b84..8a09d4d 100644 --- a/vcpkg-overlay/scale/portfile.cmake +++ b/vcpkg-overlay/scale/portfile.cmake @@ -2,10 +2,15 @@ vcpkg_check_linkage(ONLY_STATIC_LIBRARY) vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO qdrvm/scale-codec-cpp - REF ab9fb76632aa8ae98978ce3c54ed88a2fd29fd4e - SHA512 51e013a7a6e40f30f64a490251c37636b13566660c43a5245c5e5c1c3e4de3f18b81b730cacbaccfc5dd9d64b82ddec1ebcf04a47089a9be2353c82a3f3b0f25 + REF 6b3921740d8c40053a78ed843f7e105dbd978369 + SHA512 0a5464ef5e5785cd3b8af9b97e55ab57d76597c5eceb25ed5678bb95b45388fd6190c178a6670257bfdd2046b6197c330c21b961f4e4ee2e283552e519ebdc1d +) +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DJAM_COMPATIBLE=ON + -DCUSTOM_CONFIG_SUPPORT=ON ) -vcpkg_cmake_configure(SOURCE_PATH "${SOURCE_PATH}") vcpkg_cmake_install() vcpkg_cmake_config_fixup(PACKAGE_NAME "scale") file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")