Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ find_package(prometheus-cpp CONFIG REQUIRED)
find_package(Protobuf CONFIG REQUIRED)
find_package(qdrvm-crates CONFIG REQUIRED)
find_package(qtils CONFIG REQUIRED)
find_package(qdrvm-crates CONFIG REQUIRED)
find_package(RapidJSON CONFIG REQUIRED)
find_package(RocksDB CONFIG REQUIRED)
find_package(Snappy CONFIG REQUIRED)
find_package(soralog CONFIG REQUIRED)
Expand Down
167 changes: 167 additions & 0 deletions src/serde/json.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <string_view>
#include <variant>

#include <rapidjson/document.h>
#include <sszpp/lists.hpp>

#include "serde/json_fwd.hpp"
#include "types/state.hpp"

#define JSON_ASSERT(c) \
if (not(c)) throw std::runtime_error{"json"}

namespace lean::json {
struct Json {
const rapidjson::Value &v;
};

void decode(auto &v, std::string_view json_str) {
rapidjson::Document document;
document.Parse(json_str.data(), json_str.size());
decode(v, Json{document});
}

inline std::string_view decodeStr(Json json) {
JSON_ASSERT(json.v.IsString());
return {json.v.GetString(), json.v.GetStringLength()};
}

inline void decode(std::string &v, Json json) {
v = decodeStr(json);
}

template <typename T>
void decode(std::unordered_map<std::string, T> &v, Json json) {
v.clear();
JSON_ASSERT(json.v.IsObject());
for (auto it = json.v.MemberBegin(); it != json.v.MemberEnd(); ++it) {
std::string key;
decode(key, Json{it->name});
T value;
decode(value, Json{it->value});
v.emplace(std::move(key), std::move(value));
}
}

template <typename T>
void decode(std::optional<T> &v, Json json) {
v.reset();
if (not json.v.IsNull()) {
T value;
decode(value, json);
v.emplace(std::move(value));
}
}

template <typename T>
void decode(std::vector<T> &v, Json json) {
v.clear();
JSON_ASSERT(json.v.IsArray());
for (auto it = json.v.Begin(); it != json.v.End(); ++it) {
T value;
decode(value, Json{*it});
v.emplace_back(std::move(value));
}
}

template <std::integral T>
void decode(T &v, Json json) {
if constexpr (std::is_same_v<T, bool>) {
JSON_ASSERT(json.v.IsBool());
v = json.v.GetBool();
} else if constexpr (std::is_unsigned_v<T>) {
JSON_ASSERT(json.v.IsUint());
v = json.v.GetUint();
} else {
JSON_ASSERT(json.v.IsInt());
v = json.v.GetInt();
}
}

template <size_t I, typename T>
void decodeFields(const T &fields, const auto &field_names, Json json) {
JSON_ASSERT(json.v.IsObject());
auto &field = std::get<I>(fields);
auto &field_name = field_names.at(I);
auto it = json.v.FindMember(field_name.c_str());
static const rapidjson::Value json_null;
decode(field, Json{it != json.v.MemberEnd() ? it->value : json_null});
if constexpr (I + 1 < std::tuple_size_v<T>) {
decodeFields<I + 1>(fields, field_names, json);
}
}

template <typename T>
requires requires(T &v) {
v.fieldNames();
v.fields();
}
void decode(T &v, Json json) {
auto fields = v.fields();
auto &field_names = v.fieldNames();
decodeFields<0>(fields, field_names, json);
}

template <size_t N>
void decode(qtils::ByteArr<N> &v, Json json) {
v = qtils::ByteArr<N>::fromHexWithPrefix(decodeStr(json)).value();
}

template <typename T, size_t N>
void decode(ssz::list<T, N> &v, Json json) {
static std::array<std::string, 1> field_names{"data"};
decodeFields<0>(std::tie(v.data()), field_names, json);
}

template <size_t I, typename... T>
void decodeDiscriminator(std::variant<T...> &v,
const auto &type_field_values,
const std::string &type_field_value,
Json json) {
if (type_field_value == type_field_values.at(I)) {
decode(v.template emplace<I>(), json);
return;
}
if constexpr (I + 1 < sizeof...(T)) {
decodeDiscriminator<I + 1>(v, type_field_values, type_field_value, json);
} else {
JSON_ASSERT(false);
}
}

template <typename T>
requires requires(T &v) {
v.typeFieldName();
v.typeFieldValues();
}
void decode(T &v, Json json) {
auto &type_field_name = v.typeFieldName();
auto &type_field_values = v.typeFieldValues();
static std::array<std::string, 1> field_names{type_field_name};
std::string type_field_value;
decodeFields<0>(std::tie(type_field_value), field_names, json);
decodeDiscriminator<0>(v.v, type_field_values, type_field_value, json);
}

template <typename T>
requires requires(T &v) { enumValues(v); }
void decode(T &v, Json json) {
auto &enum_values = enumValues(v);
auto str = decodeStr(json);
for (auto &[enum_value, enum_str] : enum_values) {
if (str == enum_str) {
v = enum_value;
return;
}
}
JSON_ASSERT(false);
}
} // namespace lean::json
96 changes: 96 additions & 0 deletions src/serde/json_fwd.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <array>
#include <cctype>
#include <string>
#include <tuple>
#include <vector>

#define _JSON_CAMEL_NAMES_1(name) ::lean::json::toCamelCase(#name)
#define _JSON_CAMEL_NAMES_2(name, ...) \
_JSON_CAMEL_NAMES_1(name), _JSON_CAMEL_NAMES_1(__VA_ARGS__)
#define _JSON_CAMEL_NAMES_3(name, ...) \
_JSON_CAMEL_NAMES_1(name), _JSON_CAMEL_NAMES_2(__VA_ARGS__)
#define _JSON_CAMEL_NAMES_4(name, ...) \
_JSON_CAMEL_NAMES_1(name), _JSON_CAMEL_NAMES_3(__VA_ARGS__)
#define _JSON_CAMEL_NAMES_5(name, ...) \
_JSON_CAMEL_NAMES_1(name), _JSON_CAMEL_NAMES_4(__VA_ARGS__)
#define _JSON_CAMEL_NAMES_6(name, ...) \
_JSON_CAMEL_NAMES_1(name), _JSON_CAMEL_NAMES_5(__VA_ARGS__)
#define _JSON_CAMEL_NAMES_7(name, ...) \
_JSON_CAMEL_NAMES_1(name), _JSON_CAMEL_NAMES_6(__VA_ARGS__)
#define _JSON_CAMEL_NAMES_8(name, ...) \
_JSON_CAMEL_NAMES_1(name), _JSON_CAMEL_NAMES_7(__VA_ARGS__)
#define _JSON_CAMEL_NAMES_9(name, ...) \
_JSON_CAMEL_NAMES_1(name), _JSON_CAMEL_NAMES_8(__VA_ARGS__)
#define _JSON_CAMEL_NAMES_10(name, ...) \
_JSON_CAMEL_NAMES_1(name), _JSON_CAMEL_NAMES_9(__VA_ARGS__)
#define _JSON_CAMEL_NAMES_OVERLOAD( \
_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, macro, ...) \
macro
#define _JSON_CAMEL_NAMES_OVERLOAD_CALL(macro, ...) macro(__VA_ARGS__)
#define _JSON_CAMEL_NAMES(...) \
_JSON_CAMEL_NAMES_OVERLOAD_CALL( \
_JSON_CAMEL_NAMES_OVERLOAD(__VA_ARGS__, \
_JSON_CAMEL_NAMES_10, \
_JSON_CAMEL_NAMES_9, \
_JSON_CAMEL_NAMES_8, \
_JSON_CAMEL_NAMES_7, \
_JSON_CAMEL_NAMES_6, \
_JSON_CAMEL_NAMES_5, \
_JSON_CAMEL_NAMES_4, \
_JSON_CAMEL_NAMES_3, \
_JSON_CAMEL_NAMES_2, \
_JSON_CAMEL_NAMES_1), \
__VA_ARGS__)

#define JSON_CAMEL(...) \
static const auto &fieldNames() { \
static std::array field_names{_JSON_CAMEL_NAMES(__VA_ARGS__)}; \
return field_names; \
} \
auto fields() { \
return std::tie(__VA_ARGS__); \
}

#define JSON_CAMEL_DISCRIMINATOR(type_field_name, ...) \
static const std::string &typeFieldName() { \
static auto name = ::lean::json::toCamelCase(type_field_name); \
return name; \
} \
static const auto &typeFieldValues() { \
static std::array values{__VA_ARGS__}; \
return values; \
}

#define JSON_ENUM(type, ...) \
inline const auto &enumValues(const type &) { \
static std::vector<std::pair<type, std::string>> values{__VA_ARGS__}; \
return values; \
}

namespace lean::json {
inline std::string toCamelCase(std::string_view name) {
std::string camel;
bool capitalize = false;
for (auto &c : name) {
if (c == '_') {
capitalize = true;
continue;
}
if (capitalize) {
capitalize = false;
camel.push_back(std::toupper(c));
} else {
camel.push_back(c);
}
}
return camel;
}
} // namespace lean::json
3 changes: 3 additions & 0 deletions src/types/attestation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <sszpp/container.hpp>

#include "serde/json_fwd.hpp"
#include "types/attestation_data.hpp"
#include "types/validator_index.hpp"

Expand All @@ -18,5 +19,7 @@ namespace lean {

SSZ_CONT(validator_id, data);
bool operator==(const Attestation &) const = default;

JSON_CAMEL(validator_id, data);
};
} // namespace lean
3 changes: 3 additions & 0 deletions src/types/attestation_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <sszpp/container.hpp>

#include "serde/json_fwd.hpp"
#include "types/checkpoint.hpp"
#include "types/slot.hpp"

Expand All @@ -20,5 +21,7 @@ namespace lean {

SSZ_CONT(slot, head, target, source);
bool operator==(const AttestationData &) const = default;

JSON_CAMEL(slot, head, target, source);
};
} // namespace lean
3 changes: 3 additions & 0 deletions src/types/block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#pragma once

#include "serde/json_fwd.hpp"
#include "types/block_body.hpp"
#include "types/block_header.hpp"

Expand All @@ -20,6 +21,8 @@ namespace lean {
SSZ_CONT(slot, proposer_index, parent_root, state_root, body);
bool operator==(const Block &) const = default;

JSON_CAMEL(slot, proposer_index, parent_root, state_root, body);

BlockHeader getHeader() const {
BlockHeader header;
header.slot = slot;
Expand Down
3 changes: 3 additions & 0 deletions src/types/block_body.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <sszpp/container.hpp>

#include "serde/json_fwd.hpp"
#include "types/attestations.hpp"

namespace lean {
Expand All @@ -17,6 +18,8 @@ namespace lean {

SSZ_CONT(attestations);
bool operator==(const BlockBody &) const = default;

JSON_CAMEL(attestations);
};

} // namespace lean
3 changes: 3 additions & 0 deletions src/types/block_header.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <types/types.hpp>
#include <utils/custom_equality.hpp>

#include "serde/json_fwd.hpp"
#include "serde/serialization.hpp"
#include "types/block_index.hpp"

Expand Down Expand Up @@ -44,6 +45,8 @@ namespace lean {

SSZ_CONT(slot, proposer_index, parent_root, state_root, body_root);

JSON_CAMEL(slot, proposer_index, parent_root, state_root, body_root);

const HeaderHash &hash() const {
BOOST_ASSERT_MSG(hash_opt.has_value(),
"Hash must be calculated and saved before that");
Expand Down
3 changes: 3 additions & 0 deletions src/types/block_with_attestation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <sszpp/container.hpp>

#include "serde/json_fwd.hpp"
#include "types/attestation.hpp"
#include "types/block.hpp"

Expand All @@ -18,5 +19,7 @@ namespace lean {

SSZ_CONT(block, proposer_attestation);
bool operator==(const BlockWithAttestation &) const = default;

JSON_CAMEL(block, proposer_attestation);
};
} // namespace lean
3 changes: 3 additions & 0 deletions src/types/checkpoint.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <sszpp/ssz++.hpp>

#include "log/formatters/block_index_ref.hpp"
#include "serde/json_fwd.hpp"

namespace lean {

Expand All @@ -22,6 +23,8 @@ namespace lean {

SSZ_CONT(root, slot);
bool operator==(const Checkpoint &) const = default;

JSON_CAMEL(root, slot);
};

} // namespace lean
Expand Down
Loading
Loading