Skip to content

Commit

Permalink
Merge pull request #30 from jagerman/fetch-chain-ids
Browse files Browse the repository at this point in the history
Add support for fetching chainIds from all providers at once
  • Loading branch information
darcys22 authored Aug 19, 2024
2 parents 711c11e + 2e7d28f commit 33b31a9
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 93 deletions.
16 changes: 11 additions & 5 deletions include/ethyl/provider.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,23 @@ struct Client {
std::string url;
};

struct HeightInfo {
struct InfoBase {
size_t index;
bool success{false};
};
struct HeightInfo : InfoBase {
uint64_t height{0};
};
struct ChainIdInfo : InfoBase {
uint64_t chainId{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{}};
}
Expand Down Expand Up @@ -90,8 +93,8 @@ struct Provider : public std::enable_shared_from_this<Provider> {
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);
uint64_t getChainId();
void getChainIdAsync(optional_callback<uint64_t> user_cb);
std::string evm_snapshot();
void evm_snapshot_async(json_result_callback cb);
bool evm_revert(std::string_view snapshotId);
Expand Down Expand Up @@ -154,6 +157,9 @@ struct Provider : public std::enable_shared_from_this<Provider> {
std::vector<HeightInfo> getAllHeights();
void getAllHeightsAsync(std::function<void(std::vector<HeightInfo>)> user_cb);

std::vector<ChainIdInfo> getAllChainIds();
void getAllChainIdsAsync(std::function<void(std::vector<ChainIdInfo>)> user_cb);

private:

/// List of clients for interacting with the Ethereum network via RPC
Expand Down
184 changes: 97 additions & 87 deletions src/provider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct JsonResultWaiter

Provider::optional_callback<T> cb() {
return [this](std::optional<T> r) {
p.set_value(r);
p.set_value(std::move(r));
};
}

Expand All @@ -47,13 +47,9 @@ Provider::Provider()
setTimeout(DEFAULT_TIMEOUT);
}

Provider::~Provider()
{
}

void Provider::setTimeout(std::chrono::milliseconds timeout) {
std::lock_guard lk{mutex};
this->request_timeout = timeout;
request_timeout = timeout;
}

void Provider::addClient(std::string name, std::string url) {
Expand Down Expand Up @@ -118,8 +114,8 @@ std::shared_ptr<cpr::Session> Provider::get_client_session(const std::string& ur
}

bool Provider::connectToNetwork() {
// Here we can verify connection by calling some simple JSON RPC method like `net_version`
auto result = makeJsonRpcRequest("net_version", nlohmann::json::array());
// Here we can verify connection by calling some simple JSON RPC method like `eth_chainId`
auto result = makeJsonRpcRequest("eth_chainId", nlohmann::json::array());
if (result) {
log::debug(logcat, "Connected to the Ethereum network.");
} else {
Expand Down Expand Up @@ -290,41 +286,46 @@ std::string Provider::callReadFunction(
return callReadFunction(address, data, utils::decimalToHex(blockNumber, true));
}

uint32_t Provider::getNetworkChainId() {
JsonResultWaiter<uint32_t> waiter;
getNetworkChainIdAsync(waiter.cb());
uint64_t Provider::getChainId() {
JsonResultWaiter<uint64_t> waiter;
getChainIdAsync(waiter.cb());
auto result = waiter.get();

if (!result)
throw std::runtime_error("Unable to get Network ID");
return *result;
}

void Provider::getNetworkChainIdAsync(optional_callback<uint32_t> user_cb)
{
nlohmann::json params = nlohmann::json::array();
auto cb = [user_cb=std::move(user_cb)](std::optional<nlohmann::json> r) {
if (!r)
{
user_cb(std::nullopt);
return;
}
static std::optional<uint64_t> parseHexNumResponse(
const std::optional<nlohmann::json>& r, std::string_view endpoint) {
if (!r) {
log::debug(logcat, "{} result empty", endpoint);
return std::nullopt;
}
#ifndef NDEBUG
log::debug(logcat, "{} result: {}", endpoint, r->dump());
#endif

uint64_t network_id;
if (utils::parseInt(r->get<std::string>(), network_id))
{
if (network_id > std::numeric_limits<uint32_t>::max()) {
log::warning(logcat, "Network ID ({}) does not fit into 32 bit unsigned integer", network_id);
user_cb(std::nullopt);
return;
}
user_cb(static_cast<uint32_t>(network_id));
return;
}
log::warning(logcat, "Failed to parse Network ID from json rpc response.");
user_cb(std::nullopt);
};
makeJsonRpcRequest("net_version", params, std::move(cb));
try {
return utils::hexStringToU64(r->get<std::string_view>());
} catch (const std::exception& e) {
log::warning(
logcat,
"Error parsing response from {}, input: {}: {}",
endpoint,
r->dump(),
e.what());
return std::nullopt;
}
}

void Provider::getChainIdAsync(optional_callback<uint64_t> user_cb) {
makeJsonRpcRequest(
"eth_chainId",
nlohmann::json::array(),
[user_cb = std::move(user_cb)](std::optional<nlohmann::json> r) {
user_cb(parseHexNumResponse(r, "eth_chainId"));
});
}

std::string Provider::evm_snapshot() {
Expand Down Expand Up @@ -744,31 +745,11 @@ std::string Provider::getContractDeployedInLatestBlock() {
throw std::runtime_error("No contracts deployed in latest block");
}

std::optional<uint64_t> parseHeightResponse(const std::optional<nlohmann::json>& r)
{
if (!r)
{
log::debug(logcat, "eth_blockNumber result empty");
return std::nullopt;
}
log::debug(logcat, "eth_blockNumber result: {}", r->dump());

try
{
uint64_t height = utils::hexStringToU64(r->get<std::string>());
return height;
}
catch (const std::exception& e)
{
log::warning(logcat, "Error parsing response from eth_blockNumber, input: {}", r->get<std::string>());
return std::nullopt;
}
}

uint64_t Provider::getLatestHeight() {
JsonResultWaiter waiter;
getLatestHeightAsync(waiter.cb());
auto result = waiter.get();
if (!result)
throw std::runtime_error("Failed to get the latest height");
return *result;

Expand All @@ -779,56 +760,85 @@ void Provider::getLatestHeightAsync(optional_callback<uint64_t> user_cb)
nlohmann::json params = nlohmann::json::array();

auto cb = [user_cb=std::move(user_cb)](std::optional<nlohmann::json> r) {
auto height = parseHeightResponse(r);
auto height = parseHexNumResponse(r, "eth_blockNumber");
user_cb(height);
};

makeJsonRpcRequest("eth_blockNumber", params, std::move(cb));
}

std::vector<HeightInfo> Provider::getAllHeights()
{
JsonResultWaiter<std::vector<HeightInfo>> waiter;
std::vector<HeightInfo> Provider::getAllHeights() {
std::promise<std::vector<HeightInfo>> p;
auto fut = p.get_future();
auto cb = [&p](std::vector<HeightInfo> r) {
p.set_value(r);
};
getAllHeightsAsync(std::move(cb));
return fut.get();
getAllHeightsAsync([&p](std::vector<HeightInfo> r) { p.set_value(std::move(r)); });
return p.get_future().get();
}

void Provider::getAllHeightsAsync(std::function<void(std::vector<HeightInfo>)> user_cb)
{
std::vector<ChainIdInfo> Provider::getAllChainIds() {
std::promise<std::vector<ChainIdInfo>> p;
getAllChainIdsAsync([&p](std::vector<ChainIdInfo> r) { p.set_value(std::move(r)); });
return p.get_future().get();
}

namespace {

template <typename T>
struct full_request {
std::vector<HeightInfo> infos;
std::atomic<size_t> done_count{0};
std::function<void(std::vector<HeightInfo>)> user_cb;
std::atomic<size_t> remaining;
std::function<void(std::vector<T>)> user_cb;
std::vector<T> result;

explicit full_request(size_t count, std::function<void(std::vector<T>)> cb) :
remaining{count}, user_cb{std::move(cb)} {
result.resize(count);
}

void done() {
assert(remaining);
if (!--remaining)
user_cb(std::move(result));
}
};

auto req = std::make_shared<full_request>();
req->infos.resize(numClients());
req->user_cb = std::move(user_cb);
} // namespace

for (size_t i=0; i < req->infos.size(); i++)
{
req->infos[i].index = i;
auto cb = [i, req](std::optional<nlohmann::json> r){
auto height = parseHeightResponse(r);
if (height)
{
req->infos[i].height = *height;
req->infos[i].success = true;
}
if (++(req->done_count) == req->infos.size())
{
(req->user_cb)(std::move(req->infos));
void Provider::getAllHeightsAsync(std::function<void(std::vector<HeightInfo>)> user_cb) {
auto clients = numClients();
if (clients == 0)
return user_cb(std::vector<HeightInfo>{});
auto req = std::make_shared<full_request<HeightInfo>>(numClients(), std::move(user_cb));

for (size_t i = 0; i < req->result.size(); i++) {
req->result[i].index = i;
auto cb = [&info = req->result[i], req](std::optional<nlohmann::json> r) mutable {
if (auto height = parseHexNumResponse(r, "eth_blockNumber")) {
info.height = *height;
info.success = true;
}
req->done();
};
makeJsonRpcRequest("eth_blockNumber", nlohmann::json::array(), std::move(cb), {i}, false);
}
}

void Provider::getAllChainIdsAsync(std::function<void(std::vector<ChainIdInfo>)> user_cb) {
auto clients = numClients();
if (clients == 0)
return user_cb(std::vector<ChainIdInfo>{});
auto req = std::make_shared<full_request<ChainIdInfo>>(clients, std::move(user_cb));

for (size_t i = 0; i < req->result.size(); i++) {
req->result[i].index = i;
auto cb = [&info = req->result[i], req](std::optional<nlohmann::json> r) mutable {
if (auto chainid = parseHexNumResponse(r, "eth_chainId")) {
info.chainId = *chainid;
info.success = true;
}
req->done();
};
makeJsonRpcRequest("eth_chainId", nlohmann::json::array(), std::move(cb), {i}, false);
}
}

FeeData Provider::getFeeData() {
// Get latest block
nlohmann::json params = nlohmann::json::array();
Expand Down
2 changes: 1 addition & 1 deletion src/signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ void Signer::populateTransaction(Transaction& tx, std::string senderAddress) {
}

// Get network's chain ID
uint32_t networkChainId = provider->getNetworkChainId();
uint64_t networkChainId = provider->getChainId();

// Check and set chainId
if (tx.chainId != 0) {
Expand Down

0 comments on commit 33b31a9

Please sign in to comment.