Commit 4ecc0229 authored by Tsvi Sabo's avatar Tsvi Sabo

tanglescope - integrate with beast api (from 'rpchub') and boost futures

parent 7736428a
Pipeline #23503896 failed with stage
in 16 minutes and 30 seconds
workspace(name="org_iota_entangled")
workspace(name = "org_iota_entangled")
git_repository(
name="rules_iota",
remote="https://gitlab.com/iota-foundation/software/rules_iota.git",
commit="5622593910361262b248ad165aaf60bc87d0fa16")
name = "rules_iota",
commit = "cf7ffa1d71b3bc751b6cd13166fa34625673fdfd",
remote = "https://gitlab.com/iota-foundation/software/rules_iota.git",
)
android_sdk_repository(
name="androidsdk",
api_level=19, )
name = "androidsdk",
api_level = 19,
)
android_ndk_repository(
name="androidndk",
api_level=14, )
name = "androidndk",
api_level = 14,
)
load("@rules_iota//:defs.bzl", "iota_deps")
iota_deps()
http_archive(
name="io_bazel_rules_docker",
url="https://github.com/bazelbuild/rules_docker/archive/4d8ec6570a5313fb0128e2354f2bc4323685282a.zip",
strip_prefix=
"rules_docker-4d8ec6570a5313fb0128e2354f2bc4323685282a",
sha256=
"b7b9c74de652fa0a181309218ec72c126d7da09a3f3b270cc0732434617c290e")
load(
"@io_bazel_rules_docker//container:container.bzl",
"container_pull",
container_repositories = "repositories",
)
container_repositories()
container_pull(
name = "essentials",
registry = "registry.gitlab.com",
repository = "iota-foundation/software/entangled/essentials",
digest = "sha256:25af076537abcb85ef3467712a6cd8e0bc5526e06d3e86e5280d193d44c7b4cf",
)
iota_deps()
#cc_image setup
load(
"@io_bazel_rules_docker//cc:image.bzl",
_cc_image_repos = "repositories",
)
_cc_image_repos()
cc_library(
name = "beast",
srcs = ["beast.cc"],
hdrs = ["beast.h"],
visibility = ["//visibility:public"],
deps = [
":api_json",
"//common/model:transaction",
"@boost//:beast",
"@nlohmann//:json",
],
)
cc_library(
name = "api_json",
srcs = ["api_json.cc"],
hdrs = ["api_json.h"],
deps = [
":api",
"//common/helpers:digest",
"//common/model:transaction",
"@boost//:config",
"@boost//:core",
"@boost//:fusion",
"@boost//:move",
"@boost//:range",
"@boost//:thread",
"@com_github_gflags_gflags//:gflags",
"@glog",
"@nlohmann//:json",
],
)
cc_library(
name = "api",
hdrs = [
"api.h",
"messages.h",
],
visibility = ["//visibility:public"],
deps = ["@optional_lite"],
)
cc_test(
name = "tests",
srcs = glob(["tests/**/*.cc"]),
deps = [
":beast",
"@gtest",
"@gtest//:gtest_main",
],
)
#ifndef ENTANGLED_CPPCLIENT_IOTA_API_H_
#define ENTANGLED_CPPCLIENT_IOTA_API_H_
#include <chrono>
#include <cstdint>
#include <nonstd/optional.hpp>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "messages.h"
namespace cppclient {
struct Transaction {
public:
std::string hash;
std::string address;
int64_t value;
std::chrono::system_clock::time_point timestamp;
int64_t currentIndex;
int64_t lastIndex;
std::string bundleHash;
std::string trunk;
};
struct NodeInfo {
public:
std::string latestMilestone;
uint64_t latestMilestoneIndex;
uint64_t latestSolidMilestoneIndex;
};
using Bundle = std::vector<Transaction>;
/// IotaAPI class.
/// Provides an API to the tangle. The following API are available:
/// - isNodeSolid
/// - getBalances
/// - getConfirmedBundlesForAddress
/// - filterConfirmedTails
/// - filterConsistentTails
/// - findTransactions
/// - getNodeInfo
/// - getTransactions
/// - attachToTangle
/// - getTransactionsToApprove
/// - storeTransactions
/// - broadcastTransactions
class IotaAPI {
public:
virtual bool isNodeSolid() = 0;
virtual std::unordered_map<std::string, uint64_t> getBalances(
const std::vector<std::string>& addresses) = 0;
virtual std::unordered_multimap<std::string, Bundle>
getConfirmedBundlesForAddresses(
const std::vector<std::string>& addresses) = 0;
virtual std::unordered_set<std::string> filterConfirmedTails(
const std::vector<std::string>& tails,
const nonstd::optional<std::string>& reference) = 0;
virtual std::unordered_set<std::string> filterConsistentTails(
const std::vector<std::string>& tails) = 0;
virtual std::vector<std::string> findTransactions(
nonstd::optional<std::vector<std::string>> addresses,
nonstd::optional<std::vector<std::string>> bundles) = 0;
virtual NodeInfo getNodeInfo() = 0;
virtual std::vector<Transaction> getTransactions(
const std::vector<std::string>& hashes) = 0;
virtual std::vector<std::string> getTrytes(
const std::vector<std::string>& hashes) = 0;
virtual std::vector<std::string> attachToTangle(
const std::string& trunkTransaction, const std::string& branchTransaction,
size_t minWeightMagnitude, const std::vector<std::string>& trytes) = 0;
virtual GetTransactionsToApproveResponse getTransactionsToApprove(
size_t depth, const nonstd::optional<std::string>& reference = {}) = 0;
virtual bool storeTransactions(const std::vector<std::string>& trytes) = 0;
virtual GetInclusionStatesResponse getInclusionStates(
const std::vector<std::string>& trans,
const std::vector<std::string>& tips) = 0;
virtual bool broadcastTransactions(
const std::vector<std::string>& trytes) = 0;
};
} // namespace cppclient
#endif // ENTANGLED_CPPCLIENT_IOTA_API_H_
This diff is collapsed.
#ifndef ENTANGLED_CPPCLIENT_IOTA_API_JSON_H_
#define ENTANGLED_CPPCLIENT_IOTA_API_JSON_H_
#include <cstdint>
#include <nonstd/optional.hpp>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <nlohmann/json_fwd.hpp>
#include "api.h"
namespace cppclient {
/// Implementation of IotaAPI class with JSON responses
class IotaJsonAPI : virtual public IotaAPI {
public:
bool isNodeSolid() override;
std::unordered_map<std::string, uint64_t> getBalances(
const std::vector<std::string>& addresses) override;
std::unordered_multimap<std::string, Bundle> getConfirmedBundlesForAddresses(
const std::vector<std::string>& addresses) override;
std::unordered_set<std::string> filterConfirmedTails(
const std::vector<std::string>& tails,
const nonstd::optional<std::string>& reference) override;
std::vector<std::string> findTransactions(
nonstd::optional<std::vector<std::string>> addresses,
nonstd::optional<std::vector<std::string>> bundles) override;
std::unordered_set<std::string> filterConsistentTails(
const std::vector<std::string>& tails) override;
NodeInfo getNodeInfo() override;
std::vector<Transaction> getTransactions(
const std::vector<std::string>& hashes) override;
std::vector<std::string> getTrytes(
const std::vector<std::string>& hashes) override;
GetTransactionsToApproveResponse getTransactionsToApprove(
size_t depth,
const nonstd::optional<std::string>& reference = {}) override;
std::vector<std::string> attachToTangle(
const std::string& trunkTransaction, const std::string& branchTransaction,
size_t minWeightMagnitude,
const std::vector<std::string>& trytes) override;
bool storeTransactions(const std::vector<std::string>& trytes) override;
bool broadcastTransactions(const std::vector<std::string>& trytes) override;
GetInclusionStatesResponse getInclusionStates(
const std::vector<std::string>& trans,
const std::vector<std::string>& tips) override;
virtual nonstd::optional<nlohmann::json> post(
const nlohmann::json& input) = 0;
};
} // namespace cppclient
#endif // ENTANGLED_CPPCLIENT_IOTA_API_JSON_H_
#include "beast.h"
#include <nonstd/optional.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace {} // namespace
namespace cppclient {
nonstd::optional<json> BeastIotaAPI::post(const json& input) {
using tcp = boost::asio::ip::tcp;
namespace http = boost::beast::http;
boost::asio::io_context ioc;
boost::system::error_code ec;
tcp::resolver resolver{ioc};
tcp::socket socket{ioc};
json result;
try {
auto const results = resolver.resolve(_host, std::to_string(_port));
boost::asio::connect(socket, results.begin(), results.end());
http::request<http::string_body> req{http::verb::post, "/", 11};
req.set(http::field::host, _host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
req.set(http::field::content_type, "application/json");
req.set("X-IOTA-API-Version", "1");
req.body() = input.dump();
req.content_length(req.body().size());
http::write(socket, req);
boost::beast::flat_buffer buffer;
http::response<http::string_body> res;
http::read(socket, buffer, res);
result = json::parse(res.body());
socket.shutdown(tcp::socket::shutdown_both, ec);
if (ec && ec != boost::system::errc::not_connected)
throw boost::system::system_error{ec};
} catch (const std::exception& ex) {
return {};
}
return result;
}
} // namespace cppclient
#ifndef ENTANGLED_CPPCLIENT_IOTA_BEAST_H_
#define ENTANGLED_CPPCLIENT_IOTA_BEAST_H_
#include <cstdint>
#include <nonstd/optional.hpp>
#include <string>
#include <utility>
#include <vector>
#include <nlohmann/json_fwd.hpp>
#include "api.h"
#include "api_json.h"
namespace cppclient {
/// Implementation of IotaJsonAPI class. This is the actual IOTA API provider.
class BeastIotaAPI : virtual public IotaAPI, public IotaJsonAPI {
public:
BeastIotaAPI() = delete;
BeastIotaAPI(std::string host, uint32_t port)
: _host(std::move(host)), _port(port) {}
virtual ~BeastIotaAPI() {}
protected:
nonstd::optional<nlohmann::json> post(const nlohmann::json& input) override;
private:
const std::string _host;
const uint32_t _port;
};
} // namespace cppclient
#endif // ENTANGLED_CPPCLIENT_IOTA_BEAST_H_
#pragma once
#include <string>
#include <vector>
#ifndef ENTANGLED_CPPCLIENT_MESSAGES_H_
#define ENTANGLED_CPPCLIENT_MESSAGES_H_
#include <cpprest/http_client.h>
#include <cpprest/json.h>
#include <optional>
#include <nonstd/optional.hpp>
#include <string>
namespace iota {
namespace tanglescope {
namespace api {
namespace cppclient {
struct GetTransactionsToApproveResponse {
std::string trunkTransaction;
......@@ -40,36 +36,8 @@ struct GetNodeInfoResponse {
struct GetInclusionStatesResponse {
std::vector<bool> states;
std::optional<std::string> error;
nonstd::optional<std::string> error;
};
} // namespace cppclient
class IRIClient {
public:
explicit IRIClient(const std::string& url) : _url(url) {}
pplx::task<GetTransactionsToApproveResponse> getTransactionsToApprove(
int depth = 3);
pplx::task<web::json::value> broadcastTransactions(
const std::vector<std::string>&);
pplx::task<GetNodeInfoResponse> getNodeInfo();
pplx::task<GetInclusionStatesResponse> getInclusionStates(
const std::vector<std::string>& trans,
const std::vector<std::string>& tips);
pplx::task<std::vector<std::string>> getTrytes(
const std::vector<std::string>& trans);
protected:
void setupRequest(web::http::http_request&);
web::json::value stringVectorToStringJsonArray(
const std::vector<std::string>&);
private:
const std::string _url;
};
} // namespace api
} // namespace tanglescope
} // namespace iota
#endif // ENTANGLED_CPPCLIENT_MESSAGES_H_
// Copyright 2018 IOTA Foundation
#include <chrono>
#include <iostream>
#include <nonstd/optional.hpp>
#include <thread>
#include <glog/logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <nlohmann/json.hpp>
#include "cppclient/api_json.h"
using namespace testing;
using namespace cppclient;
using json = nlohmann::json;
namespace {
class IotaJsonAPITest : public ::testing::Test {};
class MockAPI : public IotaJsonAPI {
public:
json req;
json res;
MOCK_METHOD1(post, nonstd::optional<json>(const json&));
};
TEST_F(IotaJsonAPITest, GetBalances) {
MockAPI api;
std::string address(
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAA");
uint64_t balance = 1000;
std::vector<std::string> addresses = {address};
std::unordered_map<std::string, uint64_t> expected = {{address, 1000}};
json req;
req["command"] = "getBalances";
req["threshold"] = 100;
req["addresses"] = addresses;
json res;
res["balances"] = std::vector<std::string>{"1000"};
EXPECT_CALL(api, post(req)).Times(1).WillOnce(Return(res));
auto response = api.getBalances(addresses);
EXPECT_EQ(response, expected);
}
}; // namespace
// Copyright 2018 IOTA Foundation
#include <chrono>
#include <iostream>
#include <thread>
#include <glog/logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <nlohmann/json.hpp>
#include "cppclient/beast.h"
using namespace testing;
using namespace cppclient;
using json = nlohmann::json;
namespace {
class BeastIotaAPITest : public ::testing::Test {};
class MockBeast : public BeastIotaAPI {
public:
using BeastIotaAPI::BeastIotaAPI;
using BeastIotaAPI::post;
};
TEST_F(BeastIotaAPITest, InvalidHostPort) {
MockBeast beast1("localhost", 1);
MockBeast beast2("unresolvable.sometld", 41414);
json req = R"({"command":"getNodeInfo"})"_json;
auto res1 = beast1.post(req);
auto res2 = beast2.post(req);
EXPECT_FALSE(res1);
EXPECT_FALSE(res2);
}
}; // namespace
cc_library(
name='common',
include_prefix='iota/tanglescope/common',
hdrs=glob(['*.hpp']),
defines=['ZMQ_BUILD_DRAFT_API'],
srcs=glob(['*.cpp', '*.hpp']),
deps=[
'@rxcpp//:rxcpp',
'@cppzmq//:cppzmq',
'@glog//:glog',
'@cpprestsdk//:cpprestsdk',
'@libcuckoo//:libcuckoo',
'@yaml_cpp//:yaml_cpp',
name = "common",
srcs = glob([
"*.cpp",
"*.hpp",
]),
hdrs = glob(["*.hpp"]),
defines = [
"ZMQ_BUILD_DRAFT_API",
],
visibility=['//visibility:public'])
include_prefix = "iota/tanglescope/common",
visibility = ["//visibility:public"],
deps = [
"//cppclient:beast",
"@cppzmq",
"@glog",
"@libcuckoo",
"@rxcpp",
"@yaml_cpp",
],
)
cc_test(
name='common_test',
srcs=['tests/iri.cpp'],
deps=[
':common',
'@gtest//:gtest_main',
'@com_github_gflags_gflags//:gflags',
], )
name = "common_test",
srcs = ["tests/iri.cpp"],
deps = [
":common",
"@com_github_gflags_gflags//:gflags",
"@gtest//:gtest_main",
],
)
#include <cpprest/http_client.h>
#include <cpprest/json.h>
#include <glog/logging.h>
#include <chrono>
#include "api.hpp"
namespace iota {
namespace tanglescope {
namespace api {
pplx::task<GetTransactionsToApproveResponse>
IRIClient::getTransactionsToApprove(int depth) {
using namespace web;
using namespace web::http;
client::http_client client(_url);
http_request req(methods::POST);
setupRequest(req);
auto body = json::value::object();
body["command"] = json::value::string("getTransactionsToApprove");
body["depth"] = json::value::number(depth);
req.set_body(std::move(body));
return client.request(std::move(req))
.then([](http_response response) { return response.extract_json(); })
.then([](json::value js) {
return GetTransactionsToApproveResponse{
js["trunkTransaction"].as_string(),
js["branchTransaction"].as_string(),
};
});
}
pplx::task<web::json::value> IRIClient::broadcastTransactions(
const std::vector<std::string>& txs) {
using namespace web;
using namespace web::http;
client::http_client client(_url);
http_request req(methods::POST);
setupRequest(req);
auto body = json::value::object();
body["command"] = json::value::string("broadcastTransactions");
auto txArr = stringVectorToStringJsonArray(txs);
body["trytes"] = std::move(txArr);
req.set_body(std::move(body));
return client.request(std::move(req)).then([](http_response response) {
return response.extract_json();
});
}
pplx::task<GetNodeInfoResponse> IRIClient::getNodeInfo() {
using namespace web;
using namespace web::http;
client::http_client client(_url);
http_request req(methods::POST);
setupRequest(req);
auto body = json::value::object();
body["command"] = json::value::string("getNodeInfo");
req.set_body(std::move(body));
return client.request(std::move(req))
.then([](http_response response) { return response.extract_json(); })
.then([](json::value js) {
return GetNodeInfoResponse{
js["appName"].as_string(),
js["appVersion"].as_string(),
js["jreAvailableProcessors"].as_number().to_uint32(),
js["jreFreeMemory"].as_number().to_int64(),
js["jreVersion"].as_string(),
js["jreMaxMemory"].as_number().to_int64(),
js["jreTotalMemory"].as_number().to_int64(),
js["latestMilestone"].as_string(),
js["latestMilestoneIndex"].as_number().to_uint32(),
js["latestSolidSubtangleMilestone"].as_string(),
js["latestSolidSubtangleMilestoneIndex"].as_number().to_uint32(),
js["neighbors"].as_number().to_uint32(),
js["packetsQueueSize"].as_number().to_uint32(),
std::chrono::time_point<std::chrono::system_clock>(
std::chrono::seconds(js["time"].as_number().to_int64())),
js["tips"].as_number().to_uint32(),
js["transactionsToRequest"].as_number().to_uint32(),
};
});
}
pplx::task<GetInclusionStatesResponse> IRIClient::getInclusionStates(
const std::vector<std::string>& txs, const std::vector<std::string>& tips) {
using namespace web;
using namespace web::http;
client::http_client client(_url);
http_request req(methods::POST);
setupRequest(req);
auto body = json::value::object();
body["command"] = json::value::string("getInclusionStates");
auto txArr = stringVectorToStringJsonArray(txs);
body["transactions"] = std::move(txArr);
auto tipsArr = stringVectorToStringJsonArray(tips);
body["tips"] = std::move(tipsArr);
req.set_body(std::move(body));
return client.request(std::move(req))
.then([](http_response response) { return response.extract_json(); })
.then([](json::value js) {
GetInclusionStatesResponse response;
if (js["states"].is_array()) {
auto arr = js["states"].as_array();
for (auto arrIt = arr.begin(); arrIt != arr.end(); ++arrIt) {
response.states.push_back((*arrIt).as_bool());
}
} else {
response.error = js["error"].as_string();
}
return response;
});
}