🚧 converting functions

This commit is contained in:
EntireTwix 2021-06-26 19:20:41 -07:00
parent dfaac069a8
commit 9a0e59c83a
4 changed files with 164 additions and 160 deletions

View file

@ -3,10 +3,13 @@
#include <fstream> #include <fstream>
#include <shared_mutex> #include <shared_mutex>
#include <atomic> #include <atomic>
#include <drogon/HttpTypes.h>
#include "error_responses.hpp" #include "error_responses.hpp"
#include "parallel-hashmap/parallel_hashmap/phmap.h" #include "parallel-hashmap/parallel_hashmap/phmap.h"
#include "user.h" #include "user.h"
using BankResponse = std::pair<drogon::HttpStatusCode, Json::Value>;
class Bank class Bank
{ {
private: private:
@ -34,35 +37,32 @@ private:
*/ */
std::shared_mutex send_funds_l; std::shared_mutex send_funds_l;
void ChangesMade() noexcept; //called after making changes
void ChangesSaved() noexcept; //called after saving
public: public:
std::string admin_pass; std::string admin_pass;
void ChangesMade() noexcept; //called after making changes
void ChangesSaved() noexcept; //called after saving
bool GetChangeState() noexcept; bool GetChangeState() noexcept;
BankResponse GetBal(const std::string &name) const noexcept;
BankResponse GetLogs(const std::string &name) noexcept;
BankResponse SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount) noexcept;
bool VerifyPassword(const std::string &name, const std::string &attempt) const noexcept; //internally used
int_fast8_t AddUser(const std::string &name, const std::string &init_pass) noexcept; int_fast8_t AddUser(const std::string &name, const std::string &init_pass) noexcept;
int_fast8_t AdminAddUser(const std::string &attempt, std::string &&name, uint32_t init_bal, std::string &&init_pass) noexcept; int_fast8_t AdminAddUser(const std::string &attempt, std::string &&name, uint32_t init_bal, std::string &&init_pass) noexcept;
int_fast8_t DelUser(const std::string &name, const std::string &attempt) noexcept; int_fast8_t DelUser(const std::string &name, const std::string &attempt) noexcept;
int_fast8_t AdminDelUser(const std::string &name, const std::string &attempt) noexcept; int_fast8_t AdminDelUser(const std::string &name, const std::string &attempt) noexcept;
int_fast8_t SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount, const std::string &attempt) noexcept; bool Contains(const std::string &name) const noexcept; //done
int_fast8_t Contains(const std::string &name) const noexcept;
int_fast8_t AdminVerifyPass(const std::string &attempt) noexcept; int_fast8_t AdminVerifyPass(const std::string &attempt) noexcept;
int_fast8_t SetBal(const std::string &name, const std::string &attempt, uint32_t amount) noexcept; int_fast8_t SetBal(const std::string &name, const std::string &attempt, uint32_t amount) noexcept;
int_fast64_t GetBal(const std::string &name) const noexcept;
int_fast8_t VerifyPassword(const std::string &name, const std::string &attempt) const noexcept;
int_fast8_t ChangePassword(const std::string &name, const std::string &attempt, std::string &&new_pass) noexcept; int_fast8_t ChangePassword(const std::string &name, const std::string &attempt, std::string &&new_pass) noexcept;
Json::Value GetLogs(const std::string &name, const std::string &attempt) noexcept;
void Save(); void Save();
//NOT THREAD SAFE
void Load(); void Load();
}; };

View file

@ -1,15 +1,10 @@
#pragma once #pragma once
#include <drogon/HttpController.h> #include <drogon/HttpController.h>
#include "bank.h" #include "user_filter.h"
using namespace drogon; using namespace drogon;
#define req_args const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback #define req_args const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback
#define JSON(V) callback(HttpResponse::newHttpJsonResponse(JsonCast(V)));
#define PASS_HEADER req->getHeader("Password") //temporary
#define GEN_BODY \
const auto temp_req = req->getJsonObject(); \
const auto body = temp_req ? *temp_req : Json::Value();
namespace v1 namespace v1
{ {
@ -19,6 +14,11 @@ namespace v1
public: public:
api(Bank &b); api(Bank &b);
void GetBal(req_args) const;
void GetLog(req_args, const std::string &name);
void SendFunds(req_args, const std::string name) const;
void VerifyPassword(req_args) const;
void Help(req_args) const; void Help(req_args) const;
void Ping(req_args) const; void Ping(req_args) const;
void Close(req_args) const; void Close(req_args) const;
@ -26,22 +26,18 @@ namespace v1
void AdminAddUser(req_args, std::string &&name, uint32_t init_bal) const; void AdminAddUser(req_args, std::string &&name, uint32_t init_bal) const;
void DelUser(req_args, const std::string &name) const; void DelUser(req_args, const std::string &name) const;
void AdminDelUser(req_args, const std::string &name) const; void AdminDelUser(req_args, const std::string &name) const;
void SendFunds(req_args, const std::string name, const std::string to, uint32_t amount) const;
void ChangePassword(req_args, const std::string &name) const; void ChangePassword(req_args, const std::string &name) const;
void Contains(req_args, const std::string &name) const; void Contains(req_args, const std::string &name) const;
void GetBal(req_args, const std::string &name) const;
void VerifyPassword(req_args, const std::string &name) const;
void SetBal(req_args, const std::string &name, uint32_t amount) const; void SetBal(req_args, const std::string &name, uint32_t amount) const;
void AdminVerifyPass(req_args); void AdminVerifyPass(req_args);
void GetLog(req_args, const std::string &name);
METHOD_LIST_BEGIN METHOD_LIST_BEGIN
//Usage //Usage
METHOD_ADD(api::GetBal, "/{name}/bal", Get, Options); METHOD_ADD(api::GetBal, "/user/bal", Get, Options); //done
METHOD_ADD(api::GetLog, "/{name}/log", Get, Options); METHOD_ADD(api::GetLog, "/user/log", Get, Options, "UserFilter"); //snapshot not implemented
METHOD_ADD(api::SendFunds, "/{name}/send/{to}?amount={amount}", Post, Options); METHOD_ADD(api::SendFunds, "/user/transfer", Post, Options, "UserFilter"); //responses incomplete
METHOD_ADD(api::VerifyPassword, "/{name}/pass/verify", Get, Options); METHOD_ADD(api::VerifyPassword, "/{name}/pass/verify", Get, Options); //done
//Meta Usage //Meta Usage
METHOD_ADD(api::ChangePassword, "/{name}/pass/change", Patch, Options); METHOD_ADD(api::ChangePassword, "/{name}/pass/change", Patch, Options);

View file

@ -1,22 +1,24 @@
#include "bank.h" #include "bank.h"
using namespace drogon;
void Bank::ChangesMade() noexcept void Bank::ChangesMade() noexcept
{ {
if constexpr (conservative_disk_save) if constexpr (CONSERVATIVE_DISK_SAVE)
{ {
return change_flag.store(1, std::memory_order_release); return change_flag.store(1, std::memory_order_release);
} }
} }
void Bank::ChangesSaved() noexcept void Bank::ChangesSaved() noexcept
{ {
if constexpr (conservative_disk_save) if constexpr (CONSERVATIVE_DISK_SAVE)
{ {
return change_flag.store(1, std::memory_order_release); return change_flag.store(0, std::memory_order_release);
} }
} }
bool Bank::GetChangeState() noexcept bool Bank::GetChangeState() noexcept
{ {
if constexpr (conservative_disk_save) if constexpr (CONSERVATIVE_DISK_SAVE)
{ {
return change_flag.load(std::memory_order_acquire); return change_flag.load(std::memory_order_acquire);
} }
@ -26,6 +28,112 @@ bool Bank::GetChangeState() noexcept
} }
} }
BankResponse Bank::GetBal(const std::string &name) const noexcept
{
BankResponse res = {k404NotFound, "User not found"};
users.if_contains(name, [&res](const User &u) {
res = {k200OK, u.balance};
});
return res;
}
BankResponse Bank::GetLogs(const std::string &name) noexcept
{
BankResponse res{k404NotFound, "User not found"};
users.if_contains(name, [&res](const User &u) {
Json::Value temp;
for (uint32_t i = u.log.data.size(); i > 0; --i)
{
temp[i - 1]["to"] = u.log.data[u.log.data.size() - i].to;
temp[i - 1]["from"] = u.log.data[u.log.data.size() - i].from;
temp[i - 1]["amount"] = (Json::UInt)u.log.data[u.log.data.size() - i].amount;
temp[i - 1]["time"] = (Json::UInt64)u.log.data[u.log.data.size() - i].time;
}
res = {HttpStatusCode::k200OK, std::move(temp)};
});
return res;
}
BankResponse Bank::SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount) noexcept
{
//cant send money to self, from self or amount is 0
if (a_name == b_name)
{
return {k400BadRequest, "Sender and Reciever names cannot match"};
}
//cant send 0
if (!amount)
{
return {k400BadRequest, "Amount being sent cannot be 0"};
}
//as first modify_if checks a_name and grabs unique lock
if (!Contains(b_name))
{
return {k404NotFound, "Reciever does not exist"};
}
BankResponse state = {k404NotFound, "Sender does not exist"};
if constexpr (max_log_size > 0)
{
Transaction temp(a_name, b_name, amount);
std::shared_lock<std::shared_mutex> lock{send_funds_l};
users.modify_if(a_name, [this, &temp, &state, amount](User &a) {
//if A can afford it and A's password matches attempt
if (a.balance < amount)
{
state = {ErrorResponse::InsufficientFunds, "Sender has insufficient funds"};
}
else
{
a.balance -= amount;
a.log.AddTrans(Transaction(temp));
state = {HttpStatusCode::k200OK, "Transfer successful!"};
}
});
if (state.first == HttpStatusCode::k200OK)
{
users.modify_if(b_name, [&a_name, &b_name, &temp, amount](User &b) {
b.balance += amount;
b.log.AddTrans(std::move(temp));
});
ChangesMade();
}
return state;
}
else
{
std::shared_lock<std::shared_mutex> lock{send_funds_l};
users.modify_if(a_name, [this, &state, amount](User &a) {
//if A can afford it and A's password matches attempt
if (a.balance < amount)
{
state = {ErrorResponse::InsufficientFunds, "Sender has insufficient funds"};
}
else
{
a.balance -= amount;
state = {k200OK, "Transfer successful!"};
}
});
if (state.first == k200OK)
{
users.modify_if(b_name, [&a_name, &b_name, amount](User &b) {
b.balance += amount;
});
ChangesMade();
}
return state;
}
}
bool Bank::VerifyPassword(const std::string &name, const std::string &attempt) const noexcept
{
bool res = false;
users.if_contains(name, [&res, &attempt](const User &u) {
res = (u.password == XXH3_64bits(attempt.data(), attempt.size()));
});
return res;
}
int_fast8_t Bank::AddUser(const std::string &name, const std::string &init_pass) noexcept int_fast8_t Bank::AddUser(const std::string &name, const std::string &init_pass) noexcept
{ {
if (name.size() > max_name_size) if (name.size() > max_name_size)
@ -64,7 +172,7 @@ int_fast8_t Bank::DelUser(const std::string &name, const std::string &attempt) n
{ {
std::shared_lock<std::shared_mutex> lock{size_l}; std::shared_lock<std::shared_mutex> lock{size_l};
bool state = false; bool state = false;
#if return_on_del #if RETURN_ON_DEL
uint32_t bal; uint32_t bal;
if (users.erase_if(name, [this, &bal, &name, &state, &attempt](User &u) { if (users.erase_if(name, [this, &bal, &name, &state, &attempt](User &u) {
bal = u.balance; bal = u.balance;
@ -74,7 +182,7 @@ int_fast8_t Bank::DelUser(const std::string &name, const std::string &attempt) n
return state = (XXH3_64bits(attempt.data(), attempt.size()) == u.password); return state = (XXH3_64bits(attempt.data(), attempt.size()) == u.password);
})) }))
{ {
if constexpr (return_on_del) if constexpr (RETURN_ON_DEL)
{ {
return (state) ? true : ErrorResponse::WrongPassword; return (state) ? true : ErrorResponse::WrongPassword;
} }
@ -82,7 +190,7 @@ int_fast8_t Bank::DelUser(const std::string &name, const std::string &attempt) n
{ {
if (state) if (state)
{ {
#if return_on_del #if RETURN_ON_DEL
users.modify_if(return_account, [&bal](User &u) { users.modify_if(return_account, [&bal](User &u) {
u.balance += bal; u.balance += bal;
}); });
@ -104,7 +212,7 @@ int_fast8_t Bank::AdminDelUser(const std::string &name, const std::string &attem
{ {
std::shared_lock<std::shared_mutex> lock{size_l}; std::shared_lock<std::shared_mutex> lock{size_l};
bool state = false; bool state = false;
#if return_on_del #if RETURN_ON_DEL
uint32_t bal; uint32_t bal;
if (users.erase_if(name, [this, &bal, &name, &state, &attempt](User &u) { if (users.erase_if(name, [this, &bal, &name, &state, &attempt](User &u) {
bal = u.balance; bal = u.balance;
@ -116,7 +224,7 @@ int_fast8_t Bank::AdminDelUser(const std::string &name, const std::string &attem
{ {
if (state) if (state)
{ {
#if return_on_del #if RETURN_ON_DEL
users.modify_if(return_account, [&bal](User &u) { users.modify_if(return_account, [&bal](User &u) {
u.balance += bal; u.balance += bal;
}); });
@ -134,84 +242,9 @@ int_fast8_t Bank::AdminDelUser(const std::string &name, const std::string &attem
} }
} }
int_fast8_t Bank::SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount, const std::string &attempt) noexcept bool Bank::Contains(const std::string &name) const noexcept
{ {
//cant send money to self, from self or amount is 0 return users.contains(name);
if (a_name == b_name || !amount)
{
return ErrorResponse::InvalidRequest;
}
//as first modify_if checks a_name and grabs unique lock
if (!Contains(b_name))
{
return ErrorResponse::UserNotFound;
}
int_fast8_t state = false;
if constexpr (max_log_size > 0)
{
Transaction temp(a_name, b_name, amount);
std::shared_lock<std::shared_mutex> lock{send_funds_l};
users.modify_if(a_name, [&temp, &state, amount, &attempt](User &a) {
//if A can afford it and A's password matches attempt
if (a.balance < amount)
{
state = ErrorResponse::InsufficientFunds;
}
else if (a.password != XXH3_64bits(attempt.data(), attempt.size()))
{
state = ErrorResponse::WrongPassword;
}
else
{
a.balance -= amount;
a.log.AddTrans(Transaction(temp));
state = true;
}
});
if (state > 0)
{
users.modify_if(b_name, [&a_name, &b_name, &temp, amount](User &b) {
b.balance += amount;
b.log.AddTrans(std::move(temp));
});
}
return state;
}
else
{
std::shared_lock<std::shared_mutex> lock{send_funds_l};
users.modify_if(a_name, [&state, amount, &attempt](User &a) {
//if A can afford it and A's password matches attempt
if (a.balance < amount)
{
state = ErrorResponse::InsufficientFunds;
}
else if (a.password != XXH3_64bits(attempt.data(), attempt.size()))
{
state = ErrorResponse::WrongPassword;
}
else
{
a.balance -= amount;
state = true;
}
});
if (state > 0)
{
users.modify_if(b_name, [&a_name, &b_name, amount](User &b) {
b.balance += amount;
});
}
return state;
}
}
int_fast8_t Bank::Contains(const std::string &name) const noexcept
{
return (users.contains(name)) ? true : ErrorResponse::UserNotFound;
} }
int_fast8_t Bank::AdminVerifyPass(const std::string &attempt) noexcept int_fast8_t Bank::AdminVerifyPass(const std::string &attempt) noexcept
{ {
@ -231,23 +264,7 @@ int_fast8_t Bank::SetBal(const std::string &name, const std::string &attempt, ui
? true ? true
: ErrorResponse::UserNotFound; : ErrorResponse::UserNotFound;
} }
int_fast64_t Bank::GetBal(const std::string &name) const noexcept
{
int_fast64_t res = ErrorResponse::UserNotFound;
users.if_contains(name, [&res](const User &u) {
res = u.balance;
});
return res;
}
int_fast8_t Bank::VerifyPassword(const std::string &name, const std::string &attempt) const noexcept
{
int_fast8_t res = ErrorResponse::UserNotFound;
users.if_contains(name, [&res, &attempt](const User &u) {
res = (u.password == XXH3_64bits(attempt.data(), attempt.size())) ? true : ErrorResponse::WrongPassword;
});
return res;
}
int_fast8_t Bank::ChangePassword(const std::string &name, const std::string &attempt, std::string &&new_pass) noexcept int_fast8_t Bank::ChangePassword(const std::string &name, const std::string &attempt, std::string &&new_pass) noexcept
{ {
int_fast8_t res = ErrorResponse::UserNotFound; int_fast8_t res = ErrorResponse::UserNotFound;
@ -265,36 +282,10 @@ int_fast8_t Bank::ChangePassword(const std::string &name, const std::string &att
return res; return res;
} }
Json::Value Bank::GetLogs(const std::string &name, const std::string &attempt) noexcept
{
Json::Value res;
if (!users.if_contains(name, [&res, &attempt](const User &u) {
if (u.password != XXH3_64bits(attempt.data(), attempt.size()))
{
res = ErrorResponse::WrongPassword;
}
else
{
for (uint32_t i = u.log.data.size(); i > 0; --i)
{
res[i - 1]["to"] = u.log.data[u.log.data.size() - i].to;
res[i - 1]["from"] = u.log.data[u.log.data.size() - i].from;
res[i - 1]["amount"] = (Json::UInt)u.log.data[u.log.data.size() - i].amount;
res[i - 1]["time"] = (Json::UInt64)u.log.data[u.log.data.size() - i].time;
}
}
}))
{
return ErrorResponse::UserNotFound;
}
return res;
}
void Bank::Save() void Bank::Save()
{ {
if (GetChangeState()) if (GetChangeState())
{ {
//std::cout << " Saving to disk...\n";
Json::Value temp; Json::Value temp;
//loading info into json temp //loading info into json temp
@ -320,6 +311,7 @@ void Bank::Save()
writer->write(temp, &user_save); writer->write(temp, &user_save);
user_save.close(); user_save.close();
} }
ChangesSaved();
} }
} }

View file

@ -1,5 +1,19 @@
#include "bank_api.h" #include "bank_api.h"
#define JSON(V) callback(HttpResponse::newHttpJsonResponse(JsonCast(V))); //temporary
#define PASS_HEADER req->getHeader("Password") //temporary
#define GEN_BODY \
const auto temp_req = req->getJsonObject(); \
const auto body = temp_req ? *temp_req : Json::Value();
#define RESPONSE_PARSE(R) \
auto resp = HttpResponse::newHttpJsonResponse(JsonCast(R.second)); \
resp->setStatusCode(R.first); \
callback(resp);
#define NAME_PARAM req->getParameter("name")
namespace v1 namespace v1
{ {
template <typename T> template <typename T>
@ -71,9 +85,10 @@ namespace v1
{ {
JSON(bank.AdminDelUser(name, PASS_HEADER)); JSON(bank.AdminDelUser(name, PASS_HEADER));
} }
void api::SendFunds(req_args, const std::string name, const std::string to, uint32_t amount) const void api::SendFunds(req_args, const std::string name) const
{ {
JSON(bank.SendFunds(name, to, amount, PASS_HEADER)); GEN_BODY
RESPONSE_PARSE(bank.SendFunds(NAME_PARAM, body["to"].asCString(), body["amount"].asUInt()));
} }
void api::ChangePassword(req_args, const std::string &name) const void api::ChangePassword(req_args, const std::string &name) const
{ {
@ -83,13 +98,13 @@ namespace v1
{ {
JSON(bank.Contains(name)); JSON(bank.Contains(name));
} }
void api::GetBal(req_args, const std::string &name) const void api::GetBal(req_args) const
{ {
JSON(bank.GetBal(name)); RESPONSE_PARSE(bank.GetBal(NAME_PARAM));
} }
void api::VerifyPassword(req_args, const std::string &name) const void api::VerifyPassword(req_args) const
{ {
JSON(bank.VerifyPassword(name, PASS_HEADER)); RESPONSE_PARSE(BankResponse(k200OK, true));
} }
void api::SetBal(req_args, const std::string &name, uint32_t amount) const void api::SetBal(req_args, const std::string &name, uint32_t amount) const
{ {
@ -103,11 +118,12 @@ namespace v1
{ {
if constexpr (max_log_size > 0) if constexpr (max_log_size > 0)
{ {
JSON(bank.GetLogs(name, PASS_HEADER)); RESPONSE_PARSE(bank.GetLogs(NAME_PARAM));
} }
else else
{ {
auto resp = HttpResponse::newHttpJsonResponse("Logs are Disabled"); auto resp = HttpResponse::newHttpJsonResponse("Logs are Disabled");
resp->setStatusCode(k404NotFound);
resp->setExpiredTime(0); //cached forever resp->setExpiredTime(0); //cached forever
callback(resp); callback(resp);
} }