🚧 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 <shared_mutex>
#include <atomic>
#include <drogon/HttpTypes.h>
#include "error_responses.hpp"
#include "parallel-hashmap/parallel_hashmap/phmap.h"
#include "user.h"
using BankResponse = std::pair<drogon::HttpStatusCode, Json::Value>;
class Bank
{
private:
@ -34,35 +37,32 @@ private:
*/
std::shared_mutex send_funds_l;
void ChangesMade() noexcept; //called after making changes
void ChangesSaved() noexcept; //called after saving
public:
std::string admin_pass;
void ChangesMade() noexcept; //called after making changes
void ChangesSaved() noexcept; //called after saving
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 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 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;
int_fast8_t Contains(const std::string &name) const noexcept;
bool Contains(const std::string &name) const noexcept; //done
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_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;
Json::Value GetLogs(const std::string &name, const std::string &attempt) noexcept;
void Save();
//NOT THREAD SAFE
void Load();
};

View file

@ -1,15 +1,10 @@
#pragma once
#include <drogon/HttpController.h>
#include "bank.h"
#include "user_filter.h"
using namespace drogon;
#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
{
@ -19,6 +14,11 @@ namespace v1
public:
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 Ping(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 DelUser(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 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 AdminVerifyPass(req_args);
void GetLog(req_args, const std::string &name);
METHOD_LIST_BEGIN
//Usage
METHOD_ADD(api::GetBal, "/{name}/bal", Get, Options);
METHOD_ADD(api::GetLog, "/{name}/log", Get, Options);
METHOD_ADD(api::SendFunds, "/{name}/send/{to}?amount={amount}", Post, Options);
METHOD_ADD(api::VerifyPassword, "/{name}/pass/verify", Get, Options);
METHOD_ADD(api::GetBal, "/user/bal", Get, Options); //done
METHOD_ADD(api::GetLog, "/user/log", Get, Options, "UserFilter"); //snapshot not implemented
METHOD_ADD(api::SendFunds, "/user/transfer", Post, Options, "UserFilter"); //responses incomplete
METHOD_ADD(api::VerifyPassword, "/{name}/pass/verify", Get, Options); //done
//Meta Usage
METHOD_ADD(api::ChangePassword, "/{name}/pass/change", Patch, Options);

View file

@ -1,22 +1,24 @@
#include "bank.h"
using namespace drogon;
void Bank::ChangesMade() noexcept
{
if constexpr (conservative_disk_save)
if constexpr (CONSERVATIVE_DISK_SAVE)
{
return change_flag.store(1, std::memory_order_release);
}
}
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
{
if constexpr (conservative_disk_save)
if constexpr (CONSERVATIVE_DISK_SAVE)
{
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
{
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};
bool state = false;
#if return_on_del
#if RETURN_ON_DEL
uint32_t bal;
if (users.erase_if(name, [this, &bal, &name, &state, &attempt](User &u) {
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);
}))
{
if constexpr (return_on_del)
if constexpr (RETURN_ON_DEL)
{
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 return_on_del
#if RETURN_ON_DEL
users.modify_if(return_account, [&bal](User &u) {
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};
bool state = false;
#if return_on_del
#if RETURN_ON_DEL
uint32_t bal;
if (users.erase_if(name, [this, &bal, &name, &state, &attempt](User &u) {
bal = u.balance;
@ -116,7 +224,7 @@ int_fast8_t Bank::AdminDelUser(const std::string &name, const std::string &attem
{
if (state)
{
#if return_on_del
#if RETURN_ON_DEL
users.modify_if(return_account, [&bal](User &u) {
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
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;
return users.contains(name);
}
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
: 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 res = ErrorResponse::UserNotFound;
@ -265,36 +282,10 @@ int_fast8_t Bank::ChangePassword(const std::string &name, const std::string &att
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()
{
if (GetChangeState())
{
//std::cout << " Saving to disk...\n";
Json::Value temp;
//loading info into json temp
@ -320,6 +311,7 @@ void Bank::Save()
writer->write(temp, &user_save);
user_save.close();
}
ChangesSaved();
}
}

View file

@ -1,5 +1,19 @@
#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
{
template <typename T>
@ -71,9 +85,10 @@ namespace v1
{
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
{
@ -83,13 +98,13 @@ namespace v1
{
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
{
@ -103,11 +118,12 @@ namespace v1
{
if constexpr (max_log_size > 0)
{
JSON(bank.GetLogs(name, PASS_HEADER));
RESPONSE_PARSE(bank.GetLogs(NAME_PARAM));
}
else
{
auto resp = HttpResponse::newHttpJsonResponse("Logs are Disabled");
resp->setStatusCode(k404NotFound);
resp->setExpiredTime(0); //cached forever
callback(resp);
}