mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
Implemented contract read requests. (#98)
This commit is contained in:
141
src/usr/read_req.cpp
Normal file
141
src/usr/read_req.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "../pchheader.hpp"
|
||||
#include "../hplog.hpp"
|
||||
#include "../util.hpp"
|
||||
#include "../sc.hpp"
|
||||
#include "../conf.hpp"
|
||||
#include "../jsonschema/usrmsg_helpers.hpp"
|
||||
#include "usr.hpp"
|
||||
#include "read_req.hpp"
|
||||
|
||||
namespace jusrmsg = jsonschema::usrmsg;
|
||||
|
||||
/**
|
||||
* Helper functions for serving read requests from users.
|
||||
*/
|
||||
namespace read_req
|
||||
{
|
||||
constexpr uint16_t LOOP_WAIT = 100; // Milliseconds
|
||||
bool is_shutting_down = false;
|
||||
bool init_success = false;
|
||||
std::thread read_req_thread;
|
||||
sc::execution_context contract_ctx;
|
||||
|
||||
int init()
|
||||
{
|
||||
contract_ctx.args.state_dir = conf::ctx.state_read_req_dir;
|
||||
contract_ctx.args.readonly = true;
|
||||
|
||||
read_req_thread = std::thread(read_request_processor);
|
||||
init_success = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void deinit()
|
||||
{
|
||||
if (init_success)
|
||||
{
|
||||
is_shutting_down = true;
|
||||
|
||||
// Stop the contract if running.
|
||||
sc::stop(contract_ctx);
|
||||
|
||||
read_req_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void read_request_processor()
|
||||
{
|
||||
util::mask_signal();
|
||||
|
||||
LOG_INFO << "Read request server started.";
|
||||
|
||||
// Lists of read requests submitted by users keyed by user pubkey.
|
||||
std::unordered_map<std::string, std::list<std::string>> read_requests;
|
||||
|
||||
while (!is_shutting_down)
|
||||
{
|
||||
util::sleep(LOOP_WAIT);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(usr::ctx.users_mutex);
|
||||
|
||||
// Move collected read requests from users over to local requests list.
|
||||
for (auto &[sid, user] : usr::ctx.users)
|
||||
{
|
||||
if (!user.read_requests.empty())
|
||||
{
|
||||
std::list<std::string> user_read_requests;
|
||||
user_read_requests.splice(user_read_requests.end(), user.read_requests);
|
||||
|
||||
read_requests.try_emplace(user.pubkey, std::move(user_read_requests));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!read_requests.empty())
|
||||
{
|
||||
LOG_DBG << "Processing read requests... count:" << read_requests.size();
|
||||
|
||||
// Process the read requests by executing the contract.
|
||||
if (execute_contract(read_requests) != -1)
|
||||
{
|
||||
// If contract execution was succcessful, send the outputs back to users.
|
||||
std::lock_guard<std::mutex> lock(usr::ctx.users_mutex);
|
||||
|
||||
uint32_t dispatch_count = 0;
|
||||
for (auto &[pubkey, bufpair] : contract_ctx.args.userbufs)
|
||||
{
|
||||
if (!bufpair.output.empty())
|
||||
{
|
||||
// Find the user session by user pubkey.
|
||||
const auto sess_itr = usr::ctx.sessionids.find(pubkey);
|
||||
if (sess_itr != usr::ctx.sessionids.end()) // match found
|
||||
{
|
||||
const auto user_itr = usr::ctx.users.find(sess_itr->second); // sess_itr->second is the session id.
|
||||
if (user_itr != usr::ctx.users.end()) // match found
|
||||
{
|
||||
std::string outputtosend;
|
||||
outputtosend.swap(bufpair.output);
|
||||
|
||||
std::string msg;
|
||||
jusrmsg::create_contract_read_response_container(msg, outputtosend);
|
||||
|
||||
const usr::connected_user &user = user_itr->second;
|
||||
user.session.send(msg);
|
||||
dispatch_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sc::clear_args(contract_ctx.args);
|
||||
LOG_DBG << "Dispatched read request responses. count:" << dispatch_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERR << "Contract execution for read requests failed.";
|
||||
}
|
||||
|
||||
read_requests.clear();
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO << "Read request server stopped.";
|
||||
}
|
||||
|
||||
int execute_contract(std::unordered_map<std::string, std::list<std::string>> &read_requests)
|
||||
{
|
||||
// Populate read requests to user buf map.
|
||||
for (auto &[pubkey, requests] : read_requests)
|
||||
{
|
||||
sc::contract_iobuf_pair user_bufpair;
|
||||
user_bufpair.inputs.splice(user_bufpair.inputs.end(), requests);
|
||||
|
||||
contract_ctx.args.userbufs.try_emplace(pubkey, std::move(user_bufpair));
|
||||
}
|
||||
|
||||
// Execute the contract.
|
||||
return sc::execute_contract(contract_ctx);
|
||||
}
|
||||
|
||||
} // namespace read_req
|
||||
16
src/usr/read_req.hpp
Normal file
16
src/usr/read_req.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef _HP_CONS_READ_REQ_
|
||||
#define _HP_CONS_READ_REQ_
|
||||
|
||||
namespace read_req
|
||||
{
|
||||
int init();
|
||||
|
||||
void deinit();
|
||||
|
||||
void read_request_processor();
|
||||
|
||||
int execute_contract(std::unordered_map<std::string, std::list<std::string>> &read_requests);
|
||||
|
||||
} // namespace read_req
|
||||
|
||||
#endif
|
||||
@@ -134,9 +134,27 @@ namespace usr
|
||||
{
|
||||
const char *msg_type = d[jusrmsg::FLD_TYPE].GetString();
|
||||
|
||||
// Message is a contract input message.
|
||||
if (d[jusrmsg::FLD_TYPE] == jusrmsg::MSGTYPE_CONTRACT_INPUT)
|
||||
if (d[jusrmsg::FLD_TYPE] == jusrmsg::MSGTYPE_CONTRACT_READ_REQUEST)
|
||||
{
|
||||
std::string content;
|
||||
if (jusrmsg::extract_read_request(content, d) == 0)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(ctx.users_mutex);
|
||||
|
||||
//Add to the user's pending read requests list.
|
||||
user.read_requests.push_back(std::move(content));
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
send_request_status_result(user.session, jusrmsg::STATUS_REJECTED, jusrmsg::REASON_BAD_MSG_FORMAT, msg_type, "");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (d[jusrmsg::FLD_TYPE] == jusrmsg::MSGTYPE_CONTRACT_INPUT)
|
||||
{
|
||||
// Message is a contract input message.
|
||||
|
||||
std::string contentjson;
|
||||
std::string sig;
|
||||
if (jusrmsg::extract_signed_input_container(contentjson, sig, d) == 0)
|
||||
|
||||
@@ -14,68 +14,71 @@
|
||||
namespace usr
|
||||
{
|
||||
|
||||
/**
|
||||
/**
|
||||
* Holds information about an authenticated (challenge-verified) user
|
||||
* connected to the HotPocket node.
|
||||
*/
|
||||
struct connected_user
|
||||
{
|
||||
// User binary public key
|
||||
const std::string pubkey;
|
||||
struct connected_user
|
||||
{
|
||||
// User binary public key
|
||||
const std::string pubkey;
|
||||
|
||||
// Holds the unprocessed user inputs collected from websocket.
|
||||
std::list<user_submitted_message> submitted_inputs;
|
||||
// Holds the unprocessed user inputs collected from websocket.
|
||||
std::list<user_submitted_message> submitted_inputs;
|
||||
|
||||
// Holds the websocket session of this user.
|
||||
// We don't need to own the session object since the lifetime of user and session are coupled.
|
||||
const comm::comm_session &session;
|
||||
// Holds the unprocessed read requests collected from websocket.
|
||||
std::list<std::string> read_requests;
|
||||
|
||||
/**
|
||||
// Holds the websocket session of this user.
|
||||
// We don't need to own the session object since the lifetime of user and session are coupled.
|
||||
const comm::comm_session &session;
|
||||
|
||||
/**
|
||||
* @param session The web socket session the user is connected to.
|
||||
* @param pubkey The public key of the user in binary format.
|
||||
*/
|
||||
connected_user(const comm::comm_session &session, std::string_view pubkey)
|
||||
: session(session), pubkey(pubkey)
|
||||
{
|
||||
}
|
||||
};
|
||||
connected_user(const comm::comm_session &session, std::string_view pubkey)
|
||||
: session(session), pubkey(pubkey)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* The context struct to hold global connected-users and related objects.
|
||||
*/
|
||||
struct connected_context
|
||||
{
|
||||
// Connected (authenticated) user list.
|
||||
// Map key: User socket session id (<ip:port>)
|
||||
std::unordered_map<std::string, usr::connected_user> users;
|
||||
std::mutex users_mutex; // Mutex for users access race conditions.
|
||||
struct connected_context
|
||||
{
|
||||
// Connected (authenticated) user list.
|
||||
// Map key: User socket session id (<ip:port>)
|
||||
std::unordered_map<std::string, usr::connected_user> users;
|
||||
std::mutex users_mutex; // Mutex for users access race conditions.
|
||||
|
||||
// Holds set of connected user session ids and public keys for lookups.
|
||||
// This is used for pubkey duplicate checks as well.
|
||||
// Map key: User binary pubkey
|
||||
std::unordered_map<std::string, const std::string> sessionids;
|
||||
// Holds set of connected user session ids and public keys for lookups.
|
||||
// This is used for pubkey duplicate checks as well.
|
||||
// Map key: User binary pubkey
|
||||
std::unordered_map<std::string, const std::string> sessionids;
|
||||
|
||||
comm::comm_server listener;
|
||||
};
|
||||
extern connected_context ctx;
|
||||
comm::comm_server listener;
|
||||
};
|
||||
extern connected_context ctx;
|
||||
|
||||
int init();
|
||||
int init();
|
||||
|
||||
void deinit();
|
||||
void deinit();
|
||||
|
||||
int start_listening();
|
||||
int start_listening();
|
||||
|
||||
int verify_challenge(std::string_view message, comm::comm_session &session);
|
||||
int verify_challenge(std::string_view message, comm::comm_session &session);
|
||||
|
||||
int handle_user_message(connected_user &user, std::string_view message);
|
||||
int handle_user_message(connected_user &user, std::string_view message);
|
||||
|
||||
void send_request_status_result(const comm::comm_session &session, std::string_view status, std::string_view reason, std::string_view origin_type, std::string_view origin_extra_data);
|
||||
void send_request_status_result(const comm::comm_session &session, std::string_view status, std::string_view reason, std::string_view origin_type, std::string_view origin_extra_data);
|
||||
|
||||
int add_user(const comm::comm_session &session, const std::string &pubkey);
|
||||
int add_user(const comm::comm_session &session, const std::string &pubkey);
|
||||
|
||||
int remove_user(const std::string &sessionid);
|
||||
int remove_user(const std::string &sessionid);
|
||||
|
||||
const comm::comm_session *get_session_by_pubkey(const std::string &pubkey);
|
||||
const comm::comm_session *get_session_by_pubkey(const std::string &pubkey);
|
||||
|
||||
} // namespace usr
|
||||
|
||||
|
||||
Reference in New Issue
Block a user