Skip to content

Commit

Permalink
Merge pull request #25 from tewinget/async_requests
Browse files Browse the repository at this point in the history
Async requests
  • Loading branch information
Doy-lee authored Jul 4, 2024
2 parents 1fa7623 + 0b4789b commit bb394ba
Show file tree
Hide file tree
Showing 11 changed files with 688 additions and 319 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
[submodule "external/oxen-encoding"]
path = external/oxen-encoding
url = https://github.com/oxen-io/oxen-encoding.git
[submodule "external/oxen-logging"]
path = external/oxen-logging
url = https://github.com/oxen-io/oxen-logging.git
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ target_link_libraries(
secp256k1
nlohmann_json::nlohmann_json
oxenc::oxenc
oxen::logging
ethyl-keccak
PRIVATE
gmp
Expand Down
5 changes: 5 additions & 0 deletions external/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ if(NOT TARGET oxenc::oxenc)
system_or_submodule(OXENC oxenc liboxenc>=1.1.0 oxen-encoding)
endif()

if(NOT TARGET oxen::logging)
add_subdirectory(oxen-logging)
endif()
oxen_logging_add_source_dir("${PROJECT_SOURCE_DIR}")

#
# nlohmann_json
#
Expand Down
2 changes: 1 addition & 1 deletion external/cpr
Submodule cpr updated 115 files
1 change: 1 addition & 0 deletions external/oxen-logging
Submodule oxen-logging added at 21dae0
1 change: 1 addition & 0 deletions include/ethyl/logs.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// logs.hpp
#pragma once

#include <cstdint>
#include <string>
#include <vector>
#include <optional>
Expand Down
114 changes: 84 additions & 30 deletions include/ethyl/provider.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Provider.hpp
#pragma once

#include <forward_list>
#include <string>
#include <string_view>
#include <optional>
#include <queue>
#include <chrono>
#include <mutex>

Expand All @@ -18,10 +20,6 @@ using namespace std::literals;

namespace ethyl
{
struct ReadCallData {
std::string contractAddress;
std::string data;
};

struct FeeData {
uint64_t gasPrice;
Expand All @@ -37,7 +35,29 @@ struct Client {
cpr::Url url;
};

struct Provider {
struct HeightInfo {
size_t index;
bool success{false};
uint64_t height{0};
};

struct Provider : public std::enable_shared_from_this<Provider> {

protected:
Provider();
public:

~Provider();

static std::shared_ptr<Provider> make_provider() {
return std::shared_ptr<Provider>{new Provider{}};
}

template <typename Ret>
using optional_callback = std::function<void(std::optional<Ret>)>;

using json_result_callback = optional_callback<nlohmann::json>;

/** Add a RPC backend for interacting with the Ethereum network.
*
* The provider does not ensure that no duplicates are added to the list.
Expand All @@ -49,66 +69,100 @@ struct Provider {
* @returns True if the client was added successfully. False if the `url`
* was not set.
*/
bool addClient(std::string name, std::string url);
void addClient(std::string name, std::string url);

// Updates the request timeout used for new requests
void setTimeout(std::chrono::milliseconds timeout);

// The default timeout applied (if setTimeout is not called)
static constexpr auto DEFAULT_TIMEOUT = 3s;

bool connectToNetwork();
void disconnectFromNetwork();

uint64_t getTransactionCount(std::string_view address, std::string_view blockTag);
nlohmann::json callReadFunctionJSON(const ReadCallData& callData, std::string_view blockNumber = "latest");
std::string callReadFunction(const ReadCallData& callData, std::string_view blockNumber = "latest");
std::string callReadFunction(const ReadCallData& callData, uint64_t blockNumberInt);
uint64_t getTransactionCount(std::string_view address, std::string_view blockTag);
void getTransactionCountAsync(std::string_view address, std::string_view blockTag, optional_callback<uint64_t> user_cb);
nlohmann::json callReadFunctionJSON(std::string_view address, std::string_view data, std::string_view blockNumber = "latest");
void callReadFunctionJSONAsync(std::string_view address, std::string_view data, json_result_callback user_cb, std::string_view blockNumber = "latest");
std::string callReadFunction(std::string_view address, std::string_view data, std::string_view blockNumber = "latest");
std::string callReadFunction(std::string_view address, std::string_view data, uint64_t blockNumber);

uint32_t getNetworkChainId();
void getNetworkChainIdAsync(optional_callback<uint32_t> user_cb);
std::string evm_snapshot();
void evm_snapshot_async(json_result_callback cb);
bool evm_revert(std::string_view snapshotId);

uint64_t evm_increaseTime(std::chrono::seconds seconds);

std::optional<nlohmann::json> getTransactionByHash(std::string_view transactionHash);
void getTransactionByHashAsync(std::string_view transactionHash, json_result_callback cb);
std::optional<nlohmann::json> getTransactionReceipt(std::string_view transactionHash);
void getTransactionReceiptAsync(std::string_view transactionHash, json_result_callback cb);
std::vector<LogEntry> getLogs(uint64_t fromBlock, uint64_t toBlock, std::string_view address);
std::vector<LogEntry> getLogs(uint64_t block, std::string_view address);
void getLogsAsync(uint64_t fromBlock, uint64_t toBlock, std::string_view address, optional_callback<std::vector<LogEntry>> user_cb);
void getLogsAsync(uint64_t block, std::string_view address, optional_callback<std::vector<LogEntry>> cb);
std::string getContractStorageRoot(std::string_view address, uint64_t blockNumberInt);
std::string getContractStorageRoot(std::string_view address, std::string_view blockNumber = "latest");
void getContractStorageRootAsync(std::string_view address, optional_callback<std::string> user_cb, uint64_t blockNumberInt);
void getContractStorageRootAsync(std::string_view address, optional_callback<std::string> user_cb, std::string_view blockNumber = "latest");

std::string sendTransaction(const Transaction& signedTx);
std::string sendUncheckedTransaction(const Transaction& signedTx);
void sendUncheckedTransactionAsync(const Transaction& signedTx, optional_callback<std::string> user_cb);

uint64_t waitForTransaction(std::string_view txHash, std::chrono::milliseconds timeout = 320s);
bool transactionSuccessful(std::string_view txHash, std::chrono::milliseconds timeout = 320s);
uint64_t gasUsed(std::string_view txHash, std::chrono::milliseconds timeout = 320s);
std::string getBalance(std::string_view address);
void getBalanceAsync(std::string_view address, optional_callback<std::string> user_cb);
std::string getContractDeployedInLatestBlock();

uint64_t getLatestHeight();
void getLatestHeightAsync(optional_callback<uint64_t> user_cb);
FeeData getFeeData();

size_t numClients();

std::vector<Client> getClients();

std::vector<size_t> getClientOrder();
void setClientOrder(std::vector<size_t> order);

void makeJsonRpcRequest(std::string_view method,
const nlohmann::json& params,
json_result_callback cb,
std::forward_list<size_t> client_indices = {},
bool should_try_next = true);
std::optional<nlohmann::json> makeJsonRpcRequest(std::string_view method,
const nlohmann::json& params,
std::forward_list<size_t> client_indices = {},
bool should_try_next = true);

std::vector<HeightInfo> getAllHeights();
void getAllHeightsAsync(std::function<void(std::vector<HeightInfo>)> user_cb);

private:

/// List of clients for interacting with the Ethereum network via RPC
/// The order of the clients dictates the order in which a request is
/// attempted.
std::vector<Client> clients;
std::vector<Client> clients;

/// How long the provider is to attempt a connection to the client when
/// sending a request to it. If no value is set, the default connect timeout
/// of CURL is used which is currently 300 seconds.
std::optional<std::chrono::milliseconds> connectTimeout;
/// Allows the user to specify a different order in which to try provider clients
/// if the user finds that one or more clients is performing badly. This is
/// separate from `clients` so that the order in `clients` remains stable.
std::vector<size_t> client_order;

std::map<std::string, std::queue<std::shared_ptr<cpr::Session>>> client_sessions;

// Gets or creates a cpr Session for the given URL (if it is added in clients)
// This function DOES NOT lock the mutex; it assumes the caller already has!
std::shared_ptr<cpr::Session> get_client_session(const std::string& url);

std::chrono::milliseconds request_timeout{DEFAULT_TIMEOUT};

private:
/**
* @param timeout Set a timeout for the provider to connect to current
* client it's attempting before returning a timeout failure. If this is not
* set,the timeout is the default timeout value of CURL which is 300
* seconds.
*
* If you have multiple clients it may be desired to set this value such
* that the provider can quickly evaluate the backup clients in its list on
* failure.
*/
cpr::Response makeJsonRpcRequest(std::string_view method,
const nlohmann::json& params,
std::optional<std::chrono::milliseconds> timeout);
cpr::Session session;
std::mutex mutex;
};
}; // namespace ethyl
2 changes: 1 addition & 1 deletion include/ethyl/signer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class Signer {

std::string sendTransaction(Transaction& tx, std::span<const unsigned char> seckey);

Provider provider;
std::shared_ptr<Provider> provider = Provider::make_provider();

private:
secp256k1_context* ctx;
Expand Down
Loading

0 comments on commit bb394ba

Please sign in to comment.