Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

beefy rpc #1823

Merged
merged 2 commits into from
Oct 5, 2023
Merged
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: 2 additions & 0 deletions core/api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#

add_library(api
service/beefy/rpc.cpp
service/impl/api_service_impl.cpp
service/chain/impl/chain_api_impl.cpp
service/chain/requests/get_block_hash.cpp
Expand All @@ -15,6 +16,7 @@ add_library(api
service/child_state/requests/get_keys.cpp
service/child_state/requests/get_keys_paged.cpp
service/child_state/requests/get_storage.cpp
service/mmr/rpc.cpp
service/rpc/impl/rpc_api_impl.cpp
service/rpc/requests/methods.cpp
service/rpc/rpc_jrpc_processor.cpp
Expand Down
145 changes: 145 additions & 0 deletions core/api/jrpc/decode_args.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/**
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
turuslan marked this conversation as resolved.
Show resolved Hide resolved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <jsonrpc-lean/request.h>

#include "primitives/mmr.hpp"

namespace kagome::api::details {
struct LoadValue {
static void throwInvalidType() {
throw jsonrpc::InvalidParametersFault{"invalid argument type"};
}

static void throwInvalidValue() {
throw jsonrpc::InvalidParametersFault{"invalid argument value"};
}

template <typename T>
static T unwrap(outcome::result<T> &&r) {
if (r) {
return std::move(r.value());
}
throw jsonrpc::InvalidParametersFault{r.error().message()};
}

static auto &mapAt(const jsonrpc::Value &j, const std::string &k) {
auto &m = j.AsStruct();
if (auto it = m.find(k); it != m.end()) {
return it->second;
}
throw jsonrpc::InvalidParametersFault{};
}

template <typename T>
static void loadValue(std::optional<T> &dst, const jsonrpc::Value &src) {
if (!src.IsNil()) {
T t;
loadValue(t, src);
dst = std::move(t);
} else {
dst = std::nullopt;
}
}

template <typename SequenceContainer,
typename = typename SequenceContainer::value_type,
typename = typename SequenceContainer::iterator>
static void loadValue(SequenceContainer &dst, const jsonrpc::Value &src) {
if (!src.IsNil() && src.IsArray()) {
for (auto &v : src.AsArray()) {
typename SequenceContainer::value_type t;
loadValue(t, v);
dst.insert(dst.end(), std::move(t));
}
} else {
throwInvalidType();
}
}

template <typename T>
static std::enable_if_t<std::is_integral_v<T>, void> loadValue(
T &dst, const jsonrpc::Value &src) {
if (not src.IsInteger32() and not src.IsInteger64()) {
throwInvalidType();
}
auto num = src.AsInteger64();
if constexpr (std::is_signed_v<T>) {
if (num < std::numeric_limits<T>::min()
or num > std::numeric_limits<T>::max()) {
throwInvalidValue();
}
} else {
if (num < 0
or static_cast<uint64_t>(num) > std::numeric_limits<T>::max()) {
throwInvalidValue();
}
}
dst = num;
}

static void loadValue(bool &dst, const jsonrpc::Value &src) {
if (not src.IsBoolean()) {
throwInvalidType();
}
dst = src.AsBoolean();
}

static void loadValue(std::string &dst, const jsonrpc::Value &src) {
if (not src.IsString()) {
throwInvalidType();
}
dst = src.AsString();
}

template <size_t N>
static void loadValue(common::Blob<N> &out, const jsonrpc::Value &j) {
auto &s = j.AsString();
if (s.starts_with("0x")) {
out = unwrap(out.fromHexWithPrefix(s));
} else {
out = unwrap(out.fromHex(s));
}
}

static void loadValue(common::Buffer &out, const jsonrpc::Value &j) {
auto &s = j.AsString();
if (s.starts_with("0x")) {
out = unwrap(common::unhexWith0x(s));
} else {
out = unwrap(common::unhex(s));
}
}

static void loadValue(primitives::MmrLeavesProof &out,
const jsonrpc::Value &j) {
loadValue(out.block_hash, mapAt(j, "blockHash"));
loadValue(out.leaves, mapAt(j, "leaves"));
loadValue(out.proof, mapAt(j, "proof"));
}
};

template <size_t I, typename... T>
void decodeArgsLoop(std::tuple<T...> &args,
const jsonrpc::Request::Parameters &json) {
if constexpr (I < sizeof...(T)) {
static const jsonrpc::Value kNull;
LoadValue::loadValue(std::get<I>(args),
I < json.size() ? json.at(I) : kNull);
decodeArgsLoop<I + 1>(args, json);
}
}

template <typename... T>
void decodeArgs(std::tuple<T...> &args,
const jsonrpc::Request::Parameters &json) {
if (json.size() > sizeof...(T)) {
throw jsonrpc::InvalidParametersFault("Incorrect number of params");
}
decodeArgsLoop<0>(args, json);
}
} // namespace kagome::api::details
3 changes: 3 additions & 0 deletions core/api/jrpc/jrpc_processor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
#define KAGOME_CORE_API_JRPC_JRPC_PROCESSOR_HPP

#include <boost/noncopyable.hpp>
#include <memory>

namespace kagome::api {
class JRpcServer;

/**
* @class JRpcProcessor is base class for JSON RPC processors
*/
Expand Down
12 changes: 11 additions & 1 deletion core/api/jrpc/value_converter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "primitives/digest.hpp"
#include "primitives/event_types.hpp"
#include "primitives/extrinsic.hpp"
#include "primitives/mmr.hpp"
#include "primitives/rpc_methods.hpp"
#include "primitives/runtime_dispatch_info.hpp"
#include "primitives/version.hpp"
Expand Down Expand Up @@ -115,7 +116,9 @@ namespace kagome::api {

template <typename T>
inline jsonrpc::Value makeValue(const std::optional<T> &val) {
if (!val) return {};
if (!val) {
return {};
}
return makeValue(*val);
}

Expand Down Expand Up @@ -325,6 +328,13 @@ namespace kagome::api {
return common::hex_lower_0x(scale::encode(v.data).value());
}

inline jsonrpc::Value makeValue(const primitives::MmrLeavesProof &v) {
return jStruct{
{"blockHash", makeValue(v.block_hash)},
{"leaves", makeValue(v.leaves)},
{"proof", makeValue(v.proof)},
};
}
} // namespace kagome::api

#endif // KAGOME_CORE_API_EXTRINSIC_RESPONSE_VALUE_CONVERTER_HPP
84 changes: 3 additions & 81 deletions core/api/service/base_request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,10 @@
#include <optional>
#include <tuple>

#include "api/jrpc/decode_args.hpp"
#include "outcome/outcome.hpp"

namespace kagome::api::details {

template <size_t I, size_t Max, typename Tuple, typename F>
constexpr void loop(Tuple &t, F &&f) {
static_assert(I <= Max, "Invalid expression!");
if constexpr (!std::equal_to<size_t>()(I, Max)) {
std::forward<F>(f)(I, std::get<I>(t));
loop<I + 1, Max>(t, std::forward<F>(f));
}
}

/**
* Base for all request classes. Automatically parses request arguments to
* \tparam ArgumentTypes. They can be accessed in execute() via getParam<N>().
Expand All @@ -50,15 +41,8 @@ namespace kagome::api::details {
RequestType &operator=(RequestType &&) = delete;

outcome::result<void> init(const jsonrpc::Request::Parameters &params) {
if (params.size() <= sizeof...(ArgumentTypes)) {
loop<0, sizeof...(ArgumentTypes)>(params_,
[&](const auto ix, auto &dst) {
if (ix < params.size())
loadValue(dst, params[ix]);
});
return outcome::success();
}
throw jsonrpc::InvalidParametersFault("Incorrect number of params");
decodeArgs(params_, params);
return outcome::success();
}

template <size_t I>
Expand All @@ -68,68 +52,6 @@ namespace kagome::api::details {
"Incorrect index.");
return std::get<I>(params_);
}

private:
template <typename T>
void loadValue(std::optional<T> &dst, const jsonrpc::Value &src) {
if (!src.IsNil()) {
T t;
loadValue(t, src);
dst = std::move(t);
} else {
dst = std::nullopt;
}
}

template <typename SequenceContainer,
typename = typename SequenceContainer::value_type,
typename = typename SequenceContainer::iterator>
void loadValue(SequenceContainer &dst, const jsonrpc::Value &src) {
if (!src.IsNil() && src.IsArray()) {
for (auto &v : src.AsArray()) {
typename SequenceContainer::value_type t;
loadValue(t, v);
dst.insert(dst.end(), std::move(t));
}
} else {
throw jsonrpc::InvalidParametersFault("invalid argument type");
}
}

template <typename T>
std::enable_if_t<std::is_integral_v<T>, void> loadValue(
T &dst, const jsonrpc::Value &src) {
if (not src.IsInteger32() and not src.IsInteger64()) {
throw jsonrpc::InvalidParametersFault("invalid argument type");
}
auto num = src.AsInteger64();
if constexpr (std::is_signed_v<T>) {
if (num < std::numeric_limits<T>::min()
or num > std::numeric_limits<T>::max()) {
throw jsonrpc::InvalidParametersFault("invalid argument value");
}
} else {
if (num < 0
or static_cast<uint64_t>(num) > std::numeric_limits<T>::max()) {
throw jsonrpc::InvalidParametersFault("invalid argument value");
}
}
dst = num;
}

void loadValue(bool &dst, const jsonrpc::Value &src) {
if (not src.IsBoolean()) {
throw jsonrpc::InvalidParametersFault("invalid argument type");
}
dst = src.AsBoolean();
}

void loadValue(std::string &dst, const jsonrpc::Value &src) {
if (not src.IsString()) {
throw jsonrpc::InvalidParametersFault("invalid argument type");
}
dst = src.AsString();
}
};

} // namespace kagome::api::details
Expand Down
30 changes: 30 additions & 0 deletions core/api/service/beefy/rpc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

#include "api/service/beefy/rpc.hpp"

#include "api/jrpc/jrpc_server_impl.hpp"
#include "api/service/jrpc_fn.hpp"
#include "blockchain/block_header_repository.hpp"
#include "network/beefy/beefy.hpp"

namespace kagome::api {
BeefyRpc::BeefyRpc(
std::shared_ptr<JRpcServer> server,
LazySPtr<network::Beefy> beefy,
LazySPtr<blockchain::BlockHeaderRepository> block_header_repository)
: server_{std::move(server)},
beefy_{std::move(beefy)},
block_header_repository_{std::move(block_header_repository)} {}

void BeefyRpc::registerHandlers() {
server_->registerHandler(
"beefy_getFinalizedHead",
jrpcFn(this, [](std::shared_ptr<BeefyRpc> self) {
return self->block_header_repository_.get()->getHashByNumber(
self->beefy_.get()->finalized());
}));
}
} // namespace kagome::api
35 changes: 35 additions & 0 deletions core/api/service/beefy/rpc.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include "api/jrpc/jrpc_processor.hpp"
#include "injector/lazy.hpp"

namespace kagome::network {
class Beefy;
} // namespace kagome::network

namespace kagome::blockchain {
class BlockHeaderRepository;
} // namespace kagome::blockchain

namespace kagome::api {
class BeefyRpc : public JRpcProcessor,
public std::enable_shared_from_this<BeefyRpc> {
public:
BeefyRpc(
std::shared_ptr<JRpcServer> server,
LazySPtr<network::Beefy> beefy,
LazySPtr<blockchain::BlockHeaderRepository> block_header_repository);

void registerHandlers() override;

private:
std::shared_ptr<JRpcServer> server_;
LazySPtr<network::Beefy> beefy_;
LazySPtr<blockchain::BlockHeaderRepository> block_header_repository_;
};
} // namespace kagome::api
Loading