mirror of
https://github.com/Expand-sys/CCash
synced 2025-12-17 00:22:14 +11:00
Merge branch 'EntireTwix:Refractor' into Refractor
This commit is contained in:
commit
9318314d0c
46 changed files with 42074 additions and 493 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
.vscode
|
||||
build
|
||||
ccash_config.hpp
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -4,9 +4,6 @@
|
|||
[submodule "drogon"]
|
||||
path = third_party/drogon
|
||||
url = https://github.com/an-tao/drogon
|
||||
[submodule "third_party/xxHash"]
|
||||
path = third_party/xxHash
|
||||
url = https://github.com/Cyan4973/xxHash
|
||||
[submodule "third_party/base64"]
|
||||
path = third_party/base64
|
||||
url = https://github.com/aklomp/base64
|
||||
|
|
|
|||
|
|
@ -14,19 +14,20 @@ find_package(Threads REQUIRED)
|
|||
|
||||
add_executable(${PROJECT_NAME} main.cpp )
|
||||
|
||||
add_subdirectory(third_party/xxHash/cmake_unofficial third_party/xxHash/build EXCLUDE_FROM_ALL)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
src/json_filter.cpp
|
||||
src/admin_filter.cpp
|
||||
src/bank_api.cpp
|
||||
src/bank_resp.cpp
|
||||
src/bank.cpp
|
||||
src/change_flag.cpp
|
||||
src/json_filter.cpp
|
||||
src/log.cpp
|
||||
src/simdjson.cpp
|
||||
src/str_intrusion.cpp
|
||||
src/transaction.cpp
|
||||
src/user_filter.cpp
|
||||
src/user.cpp
|
||||
src/xxhash_str.cpp
|
||||
src/xxhash.c
|
||||
)
|
||||
|
||||
if(DEFINED USER_SAVE_LOC)
|
||||
|
|
@ -47,12 +48,6 @@ else()
|
|||
set(MAX_LOG_SIZE_VAL 100)
|
||||
endif()
|
||||
|
||||
if(DEFINED PRE_LOG_SIZE)
|
||||
set(PRE_LOG_SIZE_VAL ${PRE_LOG_SIZE})
|
||||
else()
|
||||
set(PRE_LOG_SIZE_VAL 10)
|
||||
endif()
|
||||
|
||||
if(DEFINED CONSERVATIVE_DISK_SAVE)
|
||||
set(CONSERVATIVE_DISK_SAVE_VAL ${CONSERVATIVE_DISK_SAVE})
|
||||
else()
|
||||
|
|
@ -83,7 +78,6 @@ target_include_directories(${PROJECT_NAME} PUBLIC third_party/base64/include)
|
|||
add_subdirectory(third_party/drogon)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${CMAKE_THREAD_LIBS_INIT} )
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE xxHash::xxhash)
|
||||
|
||||
#AVX2_CFLAGS=-mavx2 SSSE3_CFLAGS=-mssse3 SSE41_CFLAGS=-msse4.1 SSE42_CFLAGS=-msse4.2 AVX_CFLAGS=-mavx make lib/libbase64.o
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/third_party/base64/lib/libbase64.o)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
* [problem/solution](docs/idea.md)
|
||||
* [contributors](docs/contributors.md)
|
||||
* connected services
|
||||
* how to contribute
|
||||
* [explanation](docs/connected_services/how_to/explanation.md)
|
||||
* [language APIs](docs/connected_services/how_to/APIs.md)
|
||||
* [API endpoints](docs/connected_services/how_to/endpoints.md)
|
||||
* [language APIs](docs/connected_services/how_to/APIs.md)
|
||||
* [API endpoints](docs/connected_services/how_to/endpoints.md)
|
||||
* [existing services](docs/connected_services/existing_services.md)
|
||||
* features
|
||||
* [user side](docs/features/user_side.md)
|
||||
* [implementation](docs/features/implementation.md)
|
||||
* [building](docs/building.md)
|
||||
* [FAQ](docs/FAQ.md)
|
||||
* [contributors](docs/contributors.md)
|
||||
* [Patreon](https://www.patreon.com/twoxx) if you wanna support this and/or future projects.
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <random>
|
||||
#include "xxhash_str.h"
|
||||
#include "bank.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
|
@ -62,12 +63,17 @@ static Bank bank;
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
bank.AddUser("twix", 0, "root");
|
||||
bank.AddUser("jolly", 0, "root");
|
||||
bank.admin_account = "twix";
|
||||
|
||||
const std::string data("this string is quite long which is relevant when testing the speed of a hasing function");
|
||||
Op(std::hash<std::string>{}(data), "hash<string>: ", 1000000);
|
||||
Op(xxHashStringGen{}(data), "xxHashStringGen: ", 1000000);
|
||||
|
||||
Op_a(bank.AddUser("", 0, ""), "add user: ", 1000000, bank.DelUser(""));
|
||||
Op(bank.AddBal("twix", 1), "give bal: ", 1000000);
|
||||
Op(bank.SubBal("twix", 1), "give bal: ", 1000000);
|
||||
Op(bank.ImpactBal("twix", 1), "impact bal: ", 1000000);
|
||||
Op(bank.SetBal("twix", 1000000), "set bal: ", 1000000);
|
||||
Op(bank.SendFunds("twix", "jolly", 1), "send funds: ", 1000000);
|
||||
Op(bank.SendFunds("twix", "twix", 1), "invalid send funds: ", 1000000);
|
||||
|
|
@ -81,11 +87,13 @@ int main(int argc, char **argv)
|
|||
Op(bank.GetBal("twix"), "get bal: ", 1000000);
|
||||
Op(bank.VerifyPassword("twix", "root"), "verify pass: ", 1000000);
|
||||
Op(bank.ChangePassword("twix", "root"), "change pass: ", 1000000);
|
||||
Op(bank.GetLogs("twix"), "get logs: ", 10000);
|
||||
Op(bank.Save(), "saving: ", 1);
|
||||
#if CONSERVATIVE_DISK_SAVE
|
||||
Op(bank.GetChangeState(), "change flag: ", 10000);
|
||||
#if MAX_LOG_SIZE > 0
|
||||
Op(bank.GetLogs("twix"), "get logs: ", 1000000);
|
||||
#endif
|
||||
#if CONSERVATIVE_DISK_SAVE
|
||||
Op(bank.GetChangeState(), "change flag: ", 1000000);
|
||||
#endif
|
||||
Op(bank.Save(), "saving: ", 1);
|
||||
|
||||
//GetBal scalining test
|
||||
// std::default_random_engine generator;
|
||||
|
|
@ -93,13 +101,13 @@ int main(int argc, char **argv)
|
|||
|
||||
// for (size_t i = 0; i < 10000000; ++i)
|
||||
// {
|
||||
// bank.AddUser(std::to_string(i), "root");
|
||||
// bank.AddUser(std::to_string(i), 100000, "root");
|
||||
// if (i % 10000 == 0)
|
||||
// {
|
||||
// auto u = std::to_string(distribution(generator) * i);
|
||||
// auto u = std::to_string((int)(distribution(generator) * i));
|
||||
// Op(bank.GetBal(u), std::to_string(i) + ", ", 100000);
|
||||
// }
|
||||
// }
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
// Setting both values to 0 does not compile logging (useful for if disk/memory is very valuable)
|
||||
// Setting to 0 does not compile logging (useful for if disk/memory is very valuable)
|
||||
#define MAX_LOG_SIZE @MAX_LOG_SIZE_VAL@
|
||||
#define PRE_LOG_SIZE @PRE_LOG_SIZE_VAL@
|
||||
|
||||
//default to minecraft usernames
|
||||
constexpr unsigned min_name_size = 3;
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
[PREVIOUS PAGE](building.md)
|
||||
|
|
@ -1,14 +1,43 @@
|
|||
# Building
|
||||
[PREVIOUS PAGE](features/implementation.md) | [NEXT PAGE](FAQ.md)
|
||||
|
||||
## System Requirements
|
||||
as CCash is very lightweight it can run on practically any device but here are some tips:
|
||||
* single core machines should toggle `MULTI_THREADED` to `false`
|
||||
* if your server is sufficiently active as so that each save frequency saving is highly likely then `CONSERVATIVE_DISK_SAVE` should be toggled to `false`
|
||||
* `MAX_LOG_SIZE` should be adjusted as it takes up the most memory usage/storage of the ledger's features at ~202 bytes in memory and (size) in disk at default settings. Setting to 0 will not even compile logs
|
||||
* with no users memory usage is 8.628477 Mb
|
||||
|
||||
## Drogon Depedencies
|
||||
|
||||
### Linux
|
||||
#### Debian
|
||||
`sudo apt install libjsoncpp-dev uuid-dev openssl libssl-dev zlib1g-dev`
|
||||
```
|
||||
sudo apt install libjsoncpp-dev uuid-dev openssl libssl-dev zlib1g-dev
|
||||
```
|
||||
#### CentOS 7.5
|
||||
`yum install git gcc gcc-c++ && git clone https://github.com/Kitware/CMake && cd CMake/ && ./bootstrap && make && make install && yum install centos-release-scl && devtoolset-8 && scl enable devtoolset-8 bash && git clone https://github.com/open-source-parsers/jsoncpp && cd jsoncpp/ && mkdir build && cd build && cmake .. && make && make install && yum install libuuid-devel && yum install openssl-devel && yum install zlib-devel`
|
||||
```
|
||||
yum install git gcc gcc-c++
|
||||
git clone https://github.com/Kitware/CMake
|
||||
cd CMake/
|
||||
./bootstrap
|
||||
make
|
||||
make install
|
||||
yum install centos-release-scl devtoolset-8
|
||||
scl enable devtoolset-8 bash
|
||||
git clone https://github.com/open-source-parsers/jsoncpp
|
||||
cd jsoncpp/
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
make install
|
||||
yum install libuuid-devel openssl-devel zlib-devel
|
||||
```
|
||||
### MacOS
|
||||
`brew install jsoncpp ossp-uuid openssl zlib`
|
||||
```
|
||||
brew install jsoncpp ossp-uuid openssl zlib
|
||||
```
|
||||
|
||||
### Docker
|
||||
Docker Package can be found [here](https://github.com/EntireTwix/CCash/packages/851105)
|
||||
|
|
@ -20,22 +49,21 @@ git clone --recurse-submodule https://github.com/EntireTwix/CCash/
|
|||
cd CCash
|
||||
cd third_party/base64
|
||||
AVX2_CFLAGS=-mavx2 SSSE3_CFLAGS=-mssse3 SSE41_CFLAGS=-msse4.1 SSE42_CFLAGS=-msse4.2 AVX_CFLAGS=-mavx make lib/libbase64.o
|
||||
cd ..
|
||||
cd ../..
|
||||
mkdir build
|
||||
cd build
|
||||
```
|
||||
|
||||
### CMake Flags
|
||||
there are multiple flags responsible configuring CCash:
|
||||
| name | default | description | pros | cons |
|
||||
| :--------------------- | :--------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ----------------------------------------------------------- |
|
||||
| USER_SAVE_LOC | "../users.json" | where the users are saved | `N/A` | `N/A` |
|
||||
| DROGON_CONFIG_LOC | "../config.json" | where the config is located | `N/A` | `N/A` |
|
||||
| MAX_LOG_SIZE | 100 | max number of logs per user, last `n` transactions. If both this and pre log are toggled to 0 logs will not be compiled. | large history | higher memory usage |
|
||||
| PRE_LOG_SIZE | 10 | the amount of transactions allocated in advance | faster to not alloc each transaction | higher memory usage |
|
||||
| CONSERVATIVE_DISK_SAVE | `true` | when `true` only saves when changes are made | low # of disk operations | some atomic overhead |
|
||||
| MULTI_THREADED | `true` | when `true` the program is compiled to utilize `n` threads which corresponds to how many Cores your CPU has, plus 1 for saving | speed | memory lock overhead may be in vain on single core machines |
|
||||
| RETURN_ON_DEL_NAME | `N/A` | when defined, return on delete will be toggled and any accounts deleted will send their funds to the defined account, this prevent currency destruction | prevents destruction of currency | deleting accounts is made slower |
|
||||
| name | default | description | pros | cons |
|
||||
| :--------------------- | :--------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | ----------------------------------------------------------- |
|
||||
| USER_SAVE_LOC | "../users.json" | where the users are saved | `N/A` | `N/A` |
|
||||
| DROGON_CONFIG_LOC | "../config.json" | where the config is located | `N/A` | `N/A` |
|
||||
| MAX_LOG_SIZE | 100 | max number of logs per user, last `n` transactions. If both this and pre log are toggled to 0 logs will not be compiled. | large history | higher memory usage |
|
||||
| CONSERVATIVE_DISK_SAVE | `true` | when `true` only saves when changes are made | low # of disk operations | some atomic overhead |
|
||||
| MULTI_THREADED | `true` | when `true` the program is compiled to utilize `n` threads which corresponds to how many Cores your CPU has, plus 1 for saving | speed | memory lock overhead may be in vain on single core machines |
|
||||
| RETURN_ON_DEL_NAME | `N/A` | when defined, return on delete will be toggled and any accounts deleted will send their funds to the defined account, this prevent currency destruction | prevents destruction of currency | deleting accounts is made slower |
|
||||
|
||||
|
||||
simply running
|
||||
|
|
@ -48,8 +76,12 @@ cmake -DMULTI_THREADING=false ..
|
|||
```
|
||||
with `-D`
|
||||
|
||||
### Lastly
|
||||
### Finishing building
|
||||
lastly type in
|
||||
```
|
||||
cmake <flags of your choice or none> ..
|
||||
make -j<threads>
|
||||
```
|
||||
```
|
||||
|
||||
## Certs
|
||||
make sure to edit `config.json` adding the certificate location if you're using HTTPS, I personally use [certbot](https://certbot.eff.org/), **it is Highly recommened you secure your server**.
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
[PREVIOUS PAGE](how_to/endpoints.md) | [NEXT PAGE](../features/user_side.md)
|
||||
|
|
@ -1,2 +1,8 @@
|
|||
| language |
|
||||
| -------- |
|
||||
[PREVIOUS PAGE](explanation.md) | [NEXT PAGE](endpoints.md)
|
||||
|
||||
| language | v1 |
|
||||
| ---------------------------------------------------------------------------------------- | :----------------------: |
|
||||
| [Computer Craft](https://github.com/Reactified/rpm/blob/main/packages/ccash-api/api.lua) | :heavy_multiplication_x: |
|
||||
| [JS](https://github.com/LukeeeeBennett/ccash-client-js) | :heavy_multiplication_x: |
|
||||
| [Python](https://github.com/FearlessDoggo21/CCashPythonClient) | :heavy_multiplication_x: |
|
||||
| [Rust](https://git.stboyden.com/STBoyden/ccash-rs) | :heavy_multiplication_x: |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
# API endpoints
|
||||
[PREVIOUS PAGE](APIs.md) | [NEXT PAGE](../existing_services.md)
|
||||
|
||||
## KEY
|
||||
`Jresp` - Json Response, json must be accepted in the `Accept` header, be that via `application/json` or `*/*`, failing to do so results in error `406`
|
||||
|
||||
`Jreq` - Json Request, requires `application/json` as `content-type`, failing to do so results in error `406`
|
||||
|
||||
`U` - User, requires [basic auth](https://en.wikipedia.org/wiki/Basic_access_authentication) in the header `Authorization`. This credential must be a valid user, failing to do so results in error `401`
|
||||
|
||||
`A` - Admin, same as `U` but in addition requires username supplied be equal to the admin account username
|
||||
|
||||
:heavy_check_mark:
|
||||
:heavy_multiplication_x:
|
||||
|
||||
### Usage endpoints
|
||||
| name | purpose | json input | path | HTTP Method | correct status | return type | return value | Jresp | Jreq | A | U |
|
||||
| :------------- | ------------------------------------------------------------------------------ | -------------------------------- | ------------------------------- | :---------: | :------------: | :--------------: | :--------------------------------------------: | :----------------: | :----------------------: | :----------------------: | :----------------------: |
|
||||
| GetBal | retrieving the balance of a given user, `{name}` | `N/A` | api/v1/user/balance?name={name} | `GET` | 200 | uint32 | the user's balance | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_multiplication_x: |
|
||||
| GetLog | retrieves the logs of a given user, length varies by server configuration | `N/A` | api/v1/user/log | `GET` | 200 | array of objects | [{"to":string, "amount":uint32, "time":int64}] | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_check_mark: |
|
||||
| SendFunds | sends funds from the authenticated user to the user `{name}` given in the json | {"name":string, "amount":uint32} | api/v1/user/transfer | `POST` | 200 | uint32 | the user's balance after the transaction | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: |
|
||||
| VerifyPassword | verifies the credentials, used for connected services for ease of use | `N/A` | api/v1/user/verify_password | `POST` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_check_mark: |
|
||||
|
||||
### Usage enpoints errors
|
||||
| name | 400 | 401 | 404 | 405 | 406 |
|
||||
| :------------- | :----------------------: | :----------------------: | :----------------------: | :----------------: | :----------------: |
|
||||
| GetBal | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| GetLog | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| SendFunds | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| VerifyPassword | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
|
||||
all error responses have JSON string along with them to describe
|
||||
|
||||
### Usage endpoints support
|
||||
`v` denoting the API version
|
||||
| name | v1 |
|
||||
| :------------- | :----------------: |
|
||||
| GetBal | :heavy_check_mark: |
|
||||
| GetLog | :heavy_check_mark: |
|
||||
| SendFunds | :heavy_check_mark: |
|
||||
| VerifyPassword | :heavy_check_mark: |
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
[PREVIOUS PAGE](../../idea.md) | [NEXT PAGE](APIs.md)
|
||||
|
||||
Using the ledger's API allows (you/others) to (make/use) connected services that utilize the ledger, below is a visual represenation of connected services:
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# Contributors
|
||||
| name | work |
|
||||
| :------------------------------------------ | ----------------------- |
|
||||
| Caesay | Restful API suggestions |
|
||||
| [Luke](https://github.com/LukeeeeBennett) | Docker package |
|
||||
| [React](https://github.com/Reactified) | logo |
|
||||
| [Doggo](https://github.com/FearlessDoggo21) | Logs optimized |
|
||||
| name | work |
|
||||
| :------------------------------------------ | -------------------------- |
|
||||
| Caesay | Restful API suggestions |
|
||||
| [Luke](https://github.com/LukeeeeBennett) | Docker package |
|
||||
| [React](https://github.com/Reactified) | logo |
|
||||
| [Expand](https://github.com/Expand-sys) | fixed docker package |
|
||||
| [Doggo](https://github.com/FearlessDoggo21) | Logs optimized, Python API |
|
||||
BIN
docs/features/GetBal().png
Normal file
BIN
docs/features/GetBal().png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
|
|
@ -0,0 +1,18 @@
|
|||
[PREVIOUS PAGE](user_side.md) | [NEXT PAGE](../building.md)
|
||||
|
||||
# Implementation Features
|
||||
## Parallel Hashmap
|
||||
<!-- memory vs database -->
|
||||
<!-- phmap vs std hash map -->
|
||||
<!-- https://greg7mdp.github.io/parallel-hashmap/ -->
|
||||
### Scalability
|
||||
below is `GetBal()` being called where `x` axis grows with # of users reaching 10 million users, `y` axis is time in ns. The name given is random between 0 and max users at that time as to provide more accurate results
|
||||
.png)
|
||||
as the graph demonstrates, regardless of size GetBal remains consistent at around 39ns on my 3700x.
|
||||
## xxHash
|
||||
## base64
|
||||
## simdjson
|
||||
## drogon webframework
|
||||
## multi-threading support
|
||||
## intelligent saving
|
||||
## Backwards Compatible API
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
[PREVIOUS PAGE](../connected_services/existing_services.md) | [NEXT PAGE](implementation.md)
|
||||
|
||||
# Features
|
||||
## Performance
|
||||
## Accessibility
|
||||
## Security
|
||||
## Other
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
[PREVIOUS PAGE](../README.md) | [NEXT PAGE](connected_services/how_to/explanation.md)
|
||||
|
||||
CCash is a web server hosting a ledger for Minecraft, able to be used from anything that can interact with its Restful API, including ComputerCraft.
|
||||
|
||||
the currency model most Minecraft Servers adopt if any, is resource based, usually diamonds, this model is fraught with issues however:
|
||||
|
|
@ -20,4 +22,4 @@ or on localhost:
|
|||
|
||||
running it local to the minecraft server reduces latency for ComputerCraft connected services, fortunately CCash is sufficiently lightweight as to not impact performance on most setups.
|
||||
|
||||
**DISCLAIMER: if you are to run it locally and want to use ComputerCraft with it, make sure to add `127.0.0.1` to ComputerCraft's config section `allowed_domains`**
|
||||
**DISCLAIMER: if you are to run it locally and want to use ComputerCraft with it, make sure to add `127.0.0.1` to ComputerCraft's config section `allowed_domains`**
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
#pragma once
|
||||
#include <drogon/HttpFilter.h>
|
||||
#include <libbase64.h>
|
||||
#include "bank.h"
|
||||
|
||||
using namespace drogon;
|
||||
|
||||
class AdminFilter : public HttpFilter<AdminFilter, false>
|
||||
{
|
||||
private:
|
||||
Bank &bank;
|
||||
|
||||
public:
|
||||
AdminFilter(Bank &);
|
||||
|
||||
virtual void doFilter(const HttpRequestPtr &,
|
||||
FilterCallback &&,
|
||||
FilterChainCallback &&) override;
|
||||
};
|
||||
|
|
@ -2,15 +2,15 @@
|
|||
#include <iostream> //temporary
|
||||
#include <fstream>
|
||||
#include <shared_mutex>
|
||||
#include <drogon/HttpTypes.h>
|
||||
#include <parallel-hashmap/parallel_hashmap/phmap.h>
|
||||
#include "bank_resp.h"
|
||||
#include "user.h"
|
||||
|
||||
#if (CONSERVATIVE_DISK_SAVE && MAX_LOG_SIZE < 0) && !MULTI_THREADED
|
||||
#include "change_flag.h"
|
||||
#endif
|
||||
|
||||
using BankResponse = std::pair<drogon::HttpStatusCode, Json::Value>;
|
||||
bool ValidUsername(const std::string &name) noexcept;
|
||||
|
||||
class Bank
|
||||
{
|
||||
|
|
@ -30,7 +30,7 @@ class Bank
|
|||
private:
|
||||
#if CONSERVATIVE_DISK_SAVE
|
||||
#if MULTI_THREADED
|
||||
ChangeFlag save_flag;
|
||||
ChangeFlag<false> save_flag;
|
||||
#else
|
||||
bool save_flag = false;
|
||||
#endif
|
||||
|
|
@ -42,27 +42,29 @@ public:
|
|||
std::string admin_account;
|
||||
|
||||
size_t NumOfUsers() const noexcept;
|
||||
uint64_t NumOfLogs() const noexcept;
|
||||
size_t NumOfLogs() const noexcept;
|
||||
size_t SumBal() const noexcept;
|
||||
|
||||
#if CONSERVATIVE_DISK_SAVE
|
||||
bool GetChangeState() const noexcept;
|
||||
#endif
|
||||
|
||||
BankResponse GetBal(const std::string &name) const noexcept;
|
||||
#if MAX_LOG_SIZE > 0
|
||||
BankResponse GetLogs(const std::string &name) noexcept;
|
||||
#endif
|
||||
BankResponse SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount) noexcept;
|
||||
bool VerifyPassword(std::string_view name, std::string_view attempt) const noexcept;
|
||||
bool VerifyPassword(const std::string &name, const std::string_view &attempt) const noexcept;
|
||||
|
||||
void ChangePassword(const std::string &name, std::string &&new_pass) noexcept;
|
||||
void ChangePassword(const std::string &name, const std::string &new_pass) noexcept;
|
||||
BankResponse SetBal(const std::string &name, uint32_t amount) noexcept;
|
||||
BankResponse AddBal(const std::string &name, uint32_t amount) noexcept;
|
||||
BankResponse SubBal(const std::string &name, uint32_t amount) noexcept;
|
||||
BankResponse ImpactBal(const std::string &name, int64_t amount) noexcept;
|
||||
bool Contains(const std::string &name) const noexcept;
|
||||
bool AdminVerifyAccount(std::string_view name) noexcept;
|
||||
bool AdminVerifyAccount(const std::string &name) noexcept;
|
||||
|
||||
BankResponse AddUser(std::string &&name, uint32_t init_bal, std::string &&init_pass) noexcept;
|
||||
BankResponse AddUser(const std::string &name, uint32_t init_bal, std::string &&init_pass) noexcept;
|
||||
BankResponse DelUser(const std::string &name) noexcept;
|
||||
|
||||
void Save();
|
||||
const char *Save();
|
||||
void Load();
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
#include <drogon/HttpController.h>
|
||||
#include "str_intrusion.h"
|
||||
#include "json_filter.h"
|
||||
#include "admin_filter.h"
|
||||
#include "user_filter.h"
|
||||
|
||||
using namespace drogon;
|
||||
|
|
@ -14,65 +14,65 @@ class api : public HttpController<api, false>
|
|||
|
||||
public:
|
||||
api(Bank &b) noexcept;
|
||||
void JsonCpp(req_args) const;
|
||||
void Json(req_args) const;
|
||||
|
||||
#if API_VERSION >= 1
|
||||
void GetBal(req_args, const std::string &name) const;
|
||||
void GetLog(req_args);
|
||||
void GetLogs(req_args);
|
||||
void SendFunds(req_args) const;
|
||||
void VerifyPassword(req_args) const;
|
||||
|
||||
void ChangePassword(req_args) const;
|
||||
void AdminChangePassword(req_args) const;
|
||||
void SetBal(req_args) const;
|
||||
void AddBal(req_args) const;
|
||||
void SubBal(req_args) const;
|
||||
void ImpactBal(req_args) const;
|
||||
|
||||
void Help(req_args) const;
|
||||
void Ping(req_args) const;
|
||||
void Close(req_args) const;
|
||||
void Contains(req_args, const std::string &name) const;
|
||||
void AdminVerifyAccount(req_args) const;
|
||||
void ApiVersion(req_args) const;
|
||||
void ApiProperties(req_args) const;
|
||||
|
||||
void AddUser(req_args) const;
|
||||
void AdminAddUser(req_args) const;
|
||||
void DelUser(req_args) const;
|
||||
void AdminDelUser(req_args) const;
|
||||
|
||||
#endif
|
||||
|
||||
METHOD_LIST_BEGIN
|
||||
|
||||
#if API_VERSION >= 1
|
||||
//Usage
|
||||
METHOD_ADD(api::GetBal, "/v1/user/balance?name={name}", Get, Options);
|
||||
METHOD_ADD(api::GetBal, "/v1/user/balance?name={name}", Get, Options, "JsonFilter<false>");
|
||||
#if MAX_LOG_SIZE > 0
|
||||
METHOD_ADD(api::GetLog, "/v1/user/log", Get, Options, "UserFilter");
|
||||
METHOD_ADD(api::GetLogs, "/v1/user/log", Get, Options, "JsonFilter<false>", "UserFilter<true, false>");
|
||||
#else
|
||||
METHOD_ADD(api::GetLog, "/v1/user/log", Get, Options);
|
||||
METHOD_ADD(api::GetLogs, "/v1/user/log", Get, Options, "JsonFilter<false>");
|
||||
#endif
|
||||
METHOD_ADD(api::SendFunds, "/v1/user/transfer", Post, Options, "JsonFilter", "UserFilter"); //expects ["to"](string) and ["amount"](32 bits)
|
||||
METHOD_ADD(api::VerifyPassword, "/v1/user/verify_password", Post, Options, "UserFilter");
|
||||
METHOD_ADD(api::SendFunds, "/v1/user/transfer", Post, Options, "JsonFilter<true>", "UserFilter<true, false>"); //expects ["name"](string) and ["amount"](32 bits)
|
||||
METHOD_ADD(api::VerifyPassword, "/v1/user/verify_password", Post, Options, "UserFilter<false, false>", "JsonFilter<false>");
|
||||
|
||||
//Meta Usage
|
||||
METHOD_ADD(api::ChangePassword, "/v1/user/change_password", Patch, Options, "JsonFilter", "UserFilter"); //expects ["new_pass"](string)
|
||||
METHOD_ADD(api::AdminChangePassword, "/v1/user/change_password", Patch, Options, "JsonFilter", "AdminFilter"); //expects ["name"](string) and ["new_pass"](string)
|
||||
METHOD_ADD(api::SetBal, "/v1/admin/set_balance", Patch, Options, "JsonFilter", "AdminFilter"); //expects ["name"](string) and ["amount"](32 bits)
|
||||
METHOD_ADD(api::AddBal, "/v1/admin/add_balance", Post, Options, "JsonFilter", "AdminFilter"); //expects ["name"](string) and ["amount"](32 bits)
|
||||
METHOD_ADD(api::SubBal, "/v1/admin/sub_balance", Post, Options, "JsonFilter", "AdminFilter"); //expects ["name"](string) and ["amount"](32 bits)
|
||||
METHOD_ADD(api::ChangePassword, "/v1/user/change_password", Patch, Options, "JsonFilter<true>", "UserFilter<true, false>"); //expects ["pass"](string)
|
||||
METHOD_ADD(api::AdminChangePassword, "/v1/admin/user/change_password", Patch, Options, "JsonFilter<true>", "UserFilter<false, true>"); //expects ["name"](string) and ["pass"](string)
|
||||
METHOD_ADD(api::SetBal, "/v1/admin/set_balance", Patch, Options, "JsonFilter<true>", "UserFilter<false, true>"); //expects ["name"](string) and ["amount"](32 bits)
|
||||
METHOD_ADD(api::ImpactBal, "/v1/admin/impact_balance", Post, Options, "JsonFilter<true>", "UserFilter<false, true>"); //expects ["name"](string) and ["amount"](32 bits)
|
||||
|
||||
//System Usage
|
||||
METHOD_ADD(api::Help, "/v1/help", Get, Options);
|
||||
METHOD_ADD(api::Ping, "/v1/ping", Get, Options);
|
||||
METHOD_ADD(api::Close, "/v1/admin/shutdown", Post, Options, "AdminFilter");
|
||||
METHOD_ADD(api::Contains, "/v1/user/exists?name={name}", Get, Options);
|
||||
METHOD_ADD(api::AdminVerifyAccount, "/v1/admin/verify_account", Post, Options, "AdminFilter");
|
||||
METHOD_ADD(api::Close, "/v1/admin/shutdown", Post, Options, "UserFilter<false, true>", "JsonFilter<false>");
|
||||
METHOD_ADD(api::Contains, "/v1/user/exists?name={name}", Get, Options, "JsonFilter<false>");
|
||||
METHOD_ADD(api::AdminVerifyAccount, "/v1/admin/verify_account", Post, Options, "UserFilter<false, true>", "JsonFilter<false>");
|
||||
|
||||
//User Managment
|
||||
METHOD_ADD(api::AddUser, "/v1/user/register", Post, Options, "JsonFilter"); //expects ["name"](string) ["pass"](string)
|
||||
METHOD_ADD(api::AdminAddUser, "/v1/admin/user/register", Post, Options, "JsonFilter", "AdminFilter"); //expects ["name"](string) ["balance"](32 bits) ["pass"](string)
|
||||
METHOD_ADD(api::DelUser, "/v1/delete", Delete, Options, "UserFilter");
|
||||
METHOD_ADD(api::AdminDelUser, "/v1/admin/delete", Delete, Options, "JsonFilter", "AdminFilter"); //expects ["name"](string)
|
||||
METHOD_ADD(api::AddUser, "/v1/user/register", Post, Options); //expects ["name"](string) ["pass"](string)
|
||||
METHOD_ADD(api::AdminAddUser, "/v1/admin/user/register", Post, Options, "JsonFilter<true>", "UserFilter<false, true>"); //expects ["name"](string) ["amount"](32 bits) ["pass"](string)
|
||||
METHOD_ADD(api::DelUser, "/v1/user/delete", Delete, Options, "UserFilter<true, false>", "JsonFilter<false>");
|
||||
METHOD_ADD(api::AdminDelUser, "/v1/admin/user/delete", Delete, Options, "JsonFilter<true>", "UserFilter<false, true>"); //expects ["name"](string)
|
||||
#endif
|
||||
METHOD_ADD(api::ApiVersion, "/version", Get, Options);
|
||||
METHOD_ADD(api::ApiProperties, "/properties", Get, Options);
|
||||
|
||||
METHOD_LIST_END
|
||||
};
|
||||
12
include/bank_resp.h
Normal file
12
include/bank_resp.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <drogon/HttpTypes.h>
|
||||
#include <drogon/HttpResponse.h>
|
||||
#include <../src/HttpResponseImpl.h>
|
||||
#include <../src/HttpAppFrameworkImpl.h>
|
||||
|
||||
using BankResponse = std::pair<drogon::HttpStatusCode, std::optional<std::string>>;
|
||||
|
||||
template <>
|
||||
drogon::HttpResponsePtr drogon::toResponse(BankResponse &&data);
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
#pragma once
|
||||
#include <atomic>
|
||||
|
||||
template <bool init>
|
||||
class ChangeFlag
|
||||
{
|
||||
private:
|
||||
std::atomic<bool> change_flag = false; //if true changes have been made
|
||||
std::atomic<bool> change_flag = init; //if true changes have been made
|
||||
|
||||
public:
|
||||
ChangeFlag() noexcept;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
#pragma once
|
||||
#include <drogon/HttpFilter.h>
|
||||
#include "bank_resp.h"
|
||||
|
||||
using namespace drogon;
|
||||
|
||||
class JsonFilter : public HttpFilter<JsonFilter, false>
|
||||
template <bool check_content_type>
|
||||
class JsonFilter : public HttpFilter<JsonFilter<check_content_type>, false>
|
||||
{
|
||||
public:
|
||||
JsonFilter();
|
||||
|
|
|
|||
|
|
@ -5,12 +5,15 @@
|
|||
#include "ccash_config.hpp"
|
||||
#include "change_flag.h"
|
||||
#include "transaction.h"
|
||||
#include "simdjson.h"
|
||||
|
||||
using namespace simdjson;
|
||||
|
||||
struct Log
|
||||
{
|
||||
private:
|
||||
ChangeFlag log_flag;
|
||||
Json::Value log_snapshot;
|
||||
ChangeFlag<true> log_flag;
|
||||
std::string log_snapshot;
|
||||
|
||||
public:
|
||||
#if MAX_LOG_SIZE == 1
|
||||
|
|
@ -19,7 +22,7 @@ public:
|
|||
std::vector<Transaction> data;
|
||||
#endif
|
||||
|
||||
const Json::Value &GetLog() noexcept;
|
||||
void AddTrans(Transaction &&t) noexcept;
|
||||
const std::string &GetLogs() noexcept;
|
||||
void AddTrans(const Transaction &t) noexcept;
|
||||
Json::Value Serialize() const; // to be removed later
|
||||
};
|
||||
|
|
|
|||
23863
include/simdjson.h
Normal file
23863
include/simdjson.h
Normal file
File diff suppressed because it is too large
Load diff
10
include/str_intrusion.h
Normal file
10
include/str_intrusion.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
struct StrFromSV_Wrapper
|
||||
{
|
||||
std::string str;
|
||||
StrFromSV_Wrapper() noexcept;
|
||||
StrFromSV_Wrapper(std::string_view sv) noexcept;
|
||||
~StrFromSV_Wrapper() noexcept;
|
||||
};
|
||||
|
|
@ -9,7 +9,7 @@ struct Transaction
|
|||
uint32_t amount = 0;
|
||||
time_t time = 0;
|
||||
|
||||
Transaction();
|
||||
Transaction(const std::string &from_str, const std::string &to_str, uint32_t amount, time_t time);
|
||||
Transaction(const std::string &from_str, const std::string &to_str, uint32_t amount);
|
||||
Transaction() noexcept;
|
||||
Transaction(const std::string &from_str, const std::string &to_str, uint32_t amount, time_t time) noexcept;
|
||||
Transaction(const std::string &from_str, const std::string &to_str, uint32_t amount) noexcept;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
#pragma once
|
||||
#include <drogon/HttpFilter.h>
|
||||
#include <libbase64.h>
|
||||
#include "str_intrusion.h"
|
||||
#include "bank.h"
|
||||
|
||||
using namespace drogon;
|
||||
|
||||
class UserFilter : public HttpFilter<UserFilter, false>
|
||||
template <bool set_body_flag, bool require_admin>
|
||||
class UserFilter : public HttpFilter<UserFilter<set_body_flag, require_admin>, false>
|
||||
{
|
||||
private:
|
||||
Bank &bank;
|
||||
|
||||
public:
|
||||
UserFilter(Bank &);
|
||||
UserFilter(Bank &b);
|
||||
|
||||
virtual void doFilter(const HttpRequestPtr &,
|
||||
FilterCallback &&,
|
||||
|
|
|
|||
5325
include/xxhash.h
Normal file
5325
include/xxhash.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,9 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <xxhash.h>
|
||||
#include "xxhash.h"
|
||||
|
||||
struct xxHashStringGen
|
||||
{
|
||||
XXH64_hash_t operator()(const std::string &str) const noexcept;
|
||||
XXH64_hash_t operator()(std::string &&str) const noexcept;
|
||||
XXH64_hash_t operator()(std::string_view str) const noexcept;
|
||||
XXH64_hash_t operator()(const std::string_view &str) const noexcept;
|
||||
};
|
||||
|
|
|
|||
135
main.cpp
135
main.cpp
|
|
@ -21,98 +21,91 @@ static Bank bank;
|
|||
|
||||
void SaveSig(int s)
|
||||
{
|
||||
std::cout << "\nSaving on close...\n";
|
||||
bank.Save();
|
||||
std::cout << "\nSaving on close...\n"
|
||||
<< bank.Save();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
static_assert(bool(MAX_LOG_SIZE) == bool(PRE_LOG_SIZE), "You must either utilize both or neither logging variables.\n");
|
||||
static_assert(MAX_LOG_SIZE >= PRE_LOG_SIZE, "The maximum log size must be larger than or equal to the amount preallocated.\n");
|
||||
static_assert(!MAX_LOG_SIZE || !(MAX_LOG_SIZE % PRE_LOG_SIZE), "The maximum log size must be divisible by the preallocation size.\n");
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: sudo ./bank <admin account> <saving frequency in minutes>\n";
|
||||
return 0;
|
||||
}
|
||||
if (geteuid() != 0)
|
||||
{
|
||||
std::cerr << "ERROR: CCash MUST be ran as root\n";
|
||||
return 0;
|
||||
}
|
||||
std::cout
|
||||
<< "\nAVX : " << (__builtin_cpu_supports("avx") ? "enabled" : "disabled")
|
||||
<< "\nAVX 2 : " << (__builtin_cpu_supports("avx2") ? "enabled" : "disabled")
|
||||
<< "\nSSE 2 : " << (__builtin_cpu_supports("sse2") ? "enabled" : "disabled")
|
||||
<< "\nSSE 3 : " << (__builtin_cpu_supports("sse3") ? "enabled" : "disabled")
|
||||
<< "\nSSE 4.1 : " << (__builtin_cpu_supports("sse4.1") ? "enabled" : "disabled")
|
||||
<< "\nSSE 4.2 : " << (__builtin_cpu_supports("sse4.2") ? "enabled" : "disabled")
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: sudo ./bank <admin account> <saving frequency in minutes>\n";
|
||||
return 0;
|
||||
}
|
||||
if (geteuid() != 0)
|
||||
{
|
||||
std::cerr << "ERROR: CCash MUST be ran as root\n";
|
||||
return 0;
|
||||
}
|
||||
std::cout
|
||||
<< "\nAPI Version : " << API_VERSION
|
||||
<< "\n\nAVX : " << (__builtin_cpu_supports("avx") ? "enabled" : "disabled")
|
||||
<< "\nAVX 2 : " << (__builtin_cpu_supports("avx2") ? "enabled" : "disabled")
|
||||
<< "\nSSE 2 : " << (__builtin_cpu_supports("sse2") ? "enabled" : "disabled")
|
||||
<< "\nSSE 3 : " << (__builtin_cpu_supports("sse3") ? "enabled" : "disabled")
|
||||
<< "\nSSE 4.1 : " << (__builtin_cpu_supports("sse4.1") ? "enabled" : "disabled")
|
||||
<< "\nSSE 4.2 : " << (__builtin_cpu_supports("sse4.2") ? "enabled" : "disabled")
|
||||
#if MULTI_THREADED
|
||||
<< "\n\nThreads : " << get_nprocs() + 1
|
||||
<< "\nMulti threading : enabled";
|
||||
<< "\n\nThreads : " << get_nprocs() + 1
|
||||
<< "\nMulti threading : enabled";
|
||||
#else
|
||||
<< "\n\nThreads : " << 2
|
||||
<< "\nMulti threading : disabled";
|
||||
<< "\n\nThreads : " << 2
|
||||
<< "\nMulti threading : disabled";
|
||||
#endif
|
||||
|
||||
//Loading users from users.json
|
||||
bank.Load();
|
||||
//Loading users from users.json
|
||||
bank.Load();
|
||||
size_t num_of_logs = bank.NumOfLogs();
|
||||
size_t num_of_users = bank.NumOfUsers();
|
||||
std::cout << "\n\nLoaded " << num_of_users << " Users ~" << (float)(sizeof(User) * num_of_users) / 1048576 << "Mb"
|
||||
<< "\nLoaded " << num_of_logs << " Logs ~" << (float)(num_of_logs * (90 + 80 + (max_name_size * 2))) / 1048576 << "Mb" //90:string representation(heap), sizeof(Transaction), max_name_size*2:filled to&from(heap)
|
||||
<< "\nLoaded " << bank.SumBal() << " CSH"
|
||||
<< std::endl; //flushing before EventLoop
|
||||
|
||||
std::cout << "\n\nLoaded " << bank.NumOfUsers() << " Users"
|
||||
<< "\nLoaded " << bank.NumOfLogs() << " Logs"
|
||||
<< std::endl; //flushing before EventLoop
|
||||
//Sig handling
|
||||
struct sigaction sigIntHandler;
|
||||
|
||||
//Sig handling
|
||||
struct sigaction sigIntHandler;
|
||||
sigIntHandler.sa_handler = SaveSig;
|
||||
sigemptyset(&sigIntHandler.sa_mask);
|
||||
sigIntHandler.sa_flags = 0;
|
||||
|
||||
sigIntHandler.sa_handler = SaveSig;
|
||||
sigemptyset(&sigIntHandler.sa_mask);
|
||||
sigIntHandler.sa_flags = 0;
|
||||
sigaction(SIGINT, &sigIntHandler, NULL);
|
||||
|
||||
sigaction(SIGINT, &sigIntHandler, NULL);
|
||||
//Admin account
|
||||
bank.admin_account = argv[1];
|
||||
|
||||
//Admin account
|
||||
bank.admin_account = argv[1];
|
||||
|
||||
//Auto Saving
|
||||
const unsigned long saving_freq = std::stoul(std::string(argv[2]));
|
||||
if (saving_freq) //if saving frequency is 0 then auto saving is turned off
|
||||
{
|
||||
std::thread([saving_freq]() {
|
||||
while (1)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::minutes(saving_freq));
|
||||
std::cout << "Saving " << std::time(0) << '\n';
|
||||
if (bank.GetChangeState())
|
||||
//Auto Saving
|
||||
const unsigned long saving_freq = std::stoul(std::string(argv[2]));
|
||||
if (saving_freq) //if saving frequency is 0 then auto saving is turned off
|
||||
{
|
||||
std::thread([saving_freq]() {
|
||||
while (1)
|
||||
{
|
||||
std::cout << " to disk...\n";
|
||||
bank.Save();
|
||||
std::this_thread::sleep_for(std::chrono::minutes(saving_freq));
|
||||
std::cout << "Saving " << std::time(0) << "...\n"
|
||||
<< bank.Save();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << " no changes...\n";
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
} //destroying setup variables
|
||||
static auto API = std::make_shared<api>(bank);
|
||||
static auto user_filter_default = std::make_shared<UserFilter<true, false>>(bank);
|
||||
static auto user_filter_sparse = std::make_shared<UserFilter<false, false>>(bank);
|
||||
static auto admin_filter = std::make_shared<UserFilter<false, true>>(bank);
|
||||
static auto json_resp_and_req_filter = std::make_shared<JsonFilter<true>>();
|
||||
static auto json_resp_filter = std::make_shared<JsonFilter<false>>();
|
||||
|
||||
auto API = std::make_shared<api>(bank);
|
||||
auto user_filter = std::make_shared<UserFilter>(bank);
|
||||
auto admin_filter = std::make_shared<AdminFilter>(bank);
|
||||
auto accept_filter = std::make_shared<JsonFilter>();
|
||||
|
||||
app().registerPostHandlingAdvice(
|
||||
[](const drogon::HttpRequestPtr &req, const drogon::HttpResponsePtr &resp) {
|
||||
resp->addHeader("Access-Control-Allow-Origin", "*"); //CORS
|
||||
});
|
||||
app()
|
||||
.loadConfigFile(config_location)
|
||||
.registerFilter(user_filter)
|
||||
.registerFilter(user_filter_default)
|
||||
.registerFilter(user_filter_sparse)
|
||||
.registerFilter(admin_filter)
|
||||
.registerFilter(json_resp_and_req_filter)
|
||||
.registerFilter(json_resp_filter)
|
||||
.registerController(API)
|
||||
#if MULTI_THREADED
|
||||
.setThreadNum(get_nprocs())
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
#include "admin_filter.h"
|
||||
|
||||
AdminFilter::AdminFilter(Bank &b) : bank(b) {}
|
||||
|
||||
void AdminFilter::doFilter(const HttpRequestPtr &req,
|
||||
FilterCallback &&fcb,
|
||||
FilterChainCallback &&fccb)
|
||||
{
|
||||
std::string_view auth_header = req->getHeader("Authorization");
|
||||
if (auth_header.size() > 6)
|
||||
{
|
||||
if (auth_header.substr(0, 6) == "Basic ")
|
||||
{
|
||||
std::string_view base64_input = auth_header.substr(6);
|
||||
char base64_result[(base64_input.size() * 3) / 4];
|
||||
size_t new_sz;
|
||||
base64_decode(base64_input.data(), base64_input.size(), base64_result, &new_sz, 0);
|
||||
|
||||
std::string_view results_view(base64_result, new_sz);
|
||||
std::size_t middle = results_view.find(':');
|
||||
if (middle != std::string::npos)
|
||||
{
|
||||
std::string_view username = results_view.substr(0, middle);
|
||||
if (bank.AdminVerifyAccount(username))
|
||||
{
|
||||
std::string_view password = results_view.substr(middle + 1);
|
||||
if (bank.VerifyPassword(username, password))
|
||||
{
|
||||
fccb();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto &resp = HttpResponse::newHttpJsonResponse("Invalid Credentials");
|
||||
resp->setStatusCode(k401Unauthorized);
|
||||
fcb(resp);
|
||||
}
|
||||
182
src/bank.cpp
182
src/bank.cpp
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
using namespace drogon;
|
||||
|
||||
__attribute__((always_inline)) inline bool ValidUsrname(const std::string &name) noexcept
|
||||
bool ValidUsername(const std::string &name) noexcept
|
||||
{
|
||||
if (name.size() < min_name_size || name.size() > max_name_size)
|
||||
{
|
||||
|
|
@ -10,7 +10,7 @@ __attribute__((always_inline)) inline bool ValidUsrname(const std::string &name)
|
|||
}
|
||||
for (const char &c : name)
|
||||
{
|
||||
if (!(std::isalpha(c) || std::isdigit(c) || c == '_'))
|
||||
if (!((c >= 97 && c <= 122) || std::isdigit(c) || c == '_'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -22,12 +22,32 @@ __attribute__((always_inline)) inline bool ValidUsrname(const std::string &name)
|
|||
size_t Bank::NumOfUsers() const noexcept { return users.size(); }
|
||||
|
||||
//NOT THREAD SAFE
|
||||
uint64_t Bank::NumOfLogs() const noexcept
|
||||
size_t Bank::NumOfLogs() const noexcept
|
||||
{
|
||||
uint64_t res = 0;
|
||||
size_t res = 0;
|
||||
#if MAX_LOG_SIZE > 0
|
||||
for (const auto &u : users)
|
||||
{
|
||||
#if MAX_LOG_SIZE == 1
|
||||
if (u.second.log.data.amount)
|
||||
{
|
||||
++res;
|
||||
}
|
||||
#else
|
||||
res += u.second.log.data.size();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
//NOT THREAD SAFE
|
||||
size_t Bank::SumBal() const noexcept
|
||||
{
|
||||
size_t res = 0;
|
||||
for (const auto &u : users)
|
||||
{
|
||||
res += u.second.balance;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
@ -45,77 +65,83 @@ bool Bank::GetChangeState() const noexcept
|
|||
|
||||
BankResponse Bank::GetBal(const std::string &name) const noexcept
|
||||
{
|
||||
uint64_t res = 0;
|
||||
users.if_contains(name, [&res](const User &u) { res = u.balance + 1; });
|
||||
return res ? BankResponse(k200OK, res - 1) : BankResponse(k404NotFound, "User not found");
|
||||
uint32_t res = 0;
|
||||
if (!users.if_contains(name, [&res](const User &u) { res = u.balance; }))
|
||||
{
|
||||
return {k404NotFound, "\"User not found\""};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {k200OK, std::to_string(res)};
|
||||
}
|
||||
}
|
||||
#if MAX_LOG_SIZE > 0
|
||||
BankResponse Bank::GetLogs(const std::string &name) noexcept
|
||||
{
|
||||
BankResponse res;
|
||||
#if MAX_LOG_SIZE > 0
|
||||
if (!users.modify_if(name, [&res](User &u) { res = {k200OK, u.log.GetLog()}; }))
|
||||
if (!users.modify_if(name, [&res](User &u) { res = {k200OK, u.log.GetLogs()}; }))
|
||||
{
|
||||
return BankResponse(k404NotFound, "User not found");
|
||||
return {k404NotFound, "\"User not found\""};
|
||||
}
|
||||
else
|
||||
{
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
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"};
|
||||
return {k400BadRequest, "\"Sender and Reciever names cannot match\""};
|
||||
}
|
||||
|
||||
//cant send 0
|
||||
if (!amount)
|
||||
{
|
||||
return {k400BadRequest, "Amount being sent cannot be 0"};
|
||||
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"};
|
||||
return {k404NotFound, "\"Reciever does not exist\""};
|
||||
}
|
||||
|
||||
BankResponse state;
|
||||
std::shared_lock<std::shared_mutex> lock{save_lock}; //about 10% of this function's cost
|
||||
#if MAX_LOG_SIZE > 0
|
||||
Transaction temp(a_name, b_name, amount);
|
||||
if (!users.modify_if(a_name, [&temp, &state, amount](User &a) {
|
||||
#else
|
||||
if (!users.modify_if(a_name, [&state, amount](User &a) {
|
||||
static thread_local Transaction temp(a_name, b_name, amount);
|
||||
#endif
|
||||
if (!users.modify_if(a_name, [&state, amount](User &a) {
|
||||
//if A can afford it
|
||||
if (a.balance < amount)
|
||||
{
|
||||
state = {k400BadRequest, "Sender has insufficient funds"};
|
||||
state = {k400BadRequest, "\"Sender has insufficient funds\""};
|
||||
}
|
||||
else
|
||||
{
|
||||
a.balance -= amount;
|
||||
#if MAX_LOG_SIZE > 0
|
||||
a.log.AddTrans(Transaction(temp)); //about 40% of this function's cost
|
||||
a.log.AddTrans(temp); //about 40% of this function's cost
|
||||
#endif
|
||||
state = {k200OK, "Transfer successful!"};
|
||||
state = {k200OK, std::to_string(a.balance)};
|
||||
}
|
||||
}))
|
||||
{
|
||||
return {k404NotFound, "Sender does not exist"};
|
||||
return {k404NotFound, "\"Sender does not exist\""};
|
||||
}
|
||||
if (state.first == k200OK)
|
||||
{
|
||||
#if MAX_LOG_SIZE > 0
|
||||
users.modify_if(b_name, [&temp, amount](User &b) {
|
||||
users.modify_if(b_name, [amount](User &b) {
|
||||
b.balance += amount;
|
||||
b.log.AddTrans(std::move(temp));
|
||||
b.log.AddTrans(temp);
|
||||
}); //about 40% of this function's cost
|
||||
#else
|
||||
users.modify_if(b_name, [amount](User &b) { b.balance += amount; });
|
||||
#endif
|
||||
|
||||
#if CONSERVATIVE_DISK_SAVE
|
||||
#if MULTI_THREADED
|
||||
save_flag.SetChangesOn(); //about 5% of this function's cost
|
||||
|
|
@ -126,16 +152,15 @@ BankResponse Bank::SendFunds(const std::string &a_name, const std::string &b_nam
|
|||
}
|
||||
return state;
|
||||
}
|
||||
bool Bank::VerifyPassword(std::string_view name, std::string_view attempt) const noexcept
|
||||
bool Bank::VerifyPassword(const std::string &name, const std::string_view &attempt) const noexcept
|
||||
{
|
||||
bool res = false;
|
||||
users.if_contains(name.data(), [&res, &attempt](const User &u) { res = (u.password == xxHashStringGen{}(attempt)); });
|
||||
users.if_contains(name, [&res, &attempt](const User &u) { res = (u.password == xxHashStringGen{}(attempt)); });
|
||||
return res;
|
||||
}
|
||||
|
||||
void Bank::ChangePassword(const std::string &name, std::string &&new_pass) noexcept
|
||||
void Bank::ChangePassword(const std::string &name, const std::string &new_pass) noexcept
|
||||
{
|
||||
users.modify_if(name, [&new_pass](User &u) { u.password = xxHashStringGen{}(new_pass); });
|
||||
#if CONSERVATIVE_DISK_SAVE
|
||||
#if MULTI_THREADED
|
||||
save_flag.SetChangesOn();
|
||||
|
|
@ -143,10 +168,15 @@ void Bank::ChangePassword(const std::string &name, std::string &&new_pass) noexc
|
|||
save_flag = true;
|
||||
#endif
|
||||
#endif
|
||||
users.modify_if(name, [&new_pass](User &u) { u.password = xxHashStringGen{}(new_pass); });
|
||||
}
|
||||
BankResponse Bank::SetBal(const std::string &name, uint32_t amount) noexcept
|
||||
{
|
||||
if (users.modify_if(name, [amount](User &u) { u.balance = amount; }))
|
||||
if (!users.modify_if(name, [amount](User &u) { u.balance = amount; }))
|
||||
{
|
||||
return {k404NotFound, "\"User not found\""};
|
||||
}
|
||||
else
|
||||
{
|
||||
#if CONSERVATIVE_DISK_SAVE
|
||||
#if MULTI_THREADED
|
||||
|
|
@ -155,20 +185,17 @@ BankResponse Bank::SetBal(const std::string &name, uint32_t amount) noexcept
|
|||
save_flag = true;
|
||||
#endif
|
||||
#endif
|
||||
return {k200OK, "Balance set!"};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {k404NotFound, "User not found"};
|
||||
return {k204NoContent, std::nullopt}; //may return new balance
|
||||
}
|
||||
}
|
||||
BankResponse Bank::AddBal(const std::string &name, uint32_t amount) noexcept
|
||||
BankResponse Bank::ImpactBal(const std::string &name, int64_t amount) noexcept
|
||||
{
|
||||
if (amount)
|
||||
if (amount == 0)
|
||||
{
|
||||
return {k400BadRequest, "Amount cannot be 0"};
|
||||
return {k400BadRequest, "\"Amount cannot be 0\""};
|
||||
}
|
||||
if (users.modify_if(name, [amount](User &u) { u.balance += amount; }))
|
||||
uint32_t balance;
|
||||
if (users.modify_if(name, [&balance, amount](User &u) { balance = (u.balance < (amount * -1) ? u.balance = 0 : u.balance += amount); }))
|
||||
{
|
||||
#if CONSERVATIVE_DISK_SAVE
|
||||
#if MULTI_THREADED
|
||||
|
|
@ -177,49 +204,26 @@ BankResponse Bank::AddBal(const std::string &name, uint32_t amount) noexcept
|
|||
save_flag = true;
|
||||
#endif
|
||||
#endif
|
||||
return {k200OK, "Balance added!"};
|
||||
return {k200OK, std::to_string(balance)}; //may return new balance
|
||||
}
|
||||
else
|
||||
{
|
||||
return {k404NotFound, "User not found"};
|
||||
}
|
||||
}
|
||||
BankResponse Bank::SubBal(const std::string &name, uint32_t amount) noexcept
|
||||
{
|
||||
if (amount)
|
||||
{
|
||||
return {k400BadRequest, "Amount cannot be 0"};
|
||||
}
|
||||
if (users.modify_if(name, [amount](User &u) { amount > u.balance ? u.balance = 0 : u.balance -= amount; }))
|
||||
{
|
||||
#if CONSERVATIVE_DISK_SAVE
|
||||
#if MULTI_THREADED
|
||||
save_flag.SetChangesOn();
|
||||
#else
|
||||
save_flag = true;
|
||||
#endif
|
||||
#endif
|
||||
return {k200OK, "Balance subtracted!"};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {k404NotFound, "User not found"};
|
||||
return {k404NotFound, "\"User not found\""};
|
||||
}
|
||||
}
|
||||
bool Bank::Contains(const std::string &name) const noexcept
|
||||
{
|
||||
return users.contains(name);
|
||||
}
|
||||
bool Bank::AdminVerifyAccount(std::string_view name) noexcept
|
||||
bool Bank::AdminVerifyAccount(const std::string &name) noexcept
|
||||
{
|
||||
return (name == admin_account);
|
||||
}
|
||||
|
||||
BankResponse Bank::AddUser(std::string &&name, uint32_t init_bal, std::string &&init_pass) noexcept
|
||||
BankResponse Bank::AddUser(const std::string &name, uint32_t init_bal, std::string &&init_pass) noexcept
|
||||
{
|
||||
if (!ValidUsrname(name))
|
||||
if (!ValidUsername(name))
|
||||
{
|
||||
return {k400BadRequest, "Invalid Name, breaks size and/or character restrictions"};
|
||||
return {k400BadRequest, "\"Invalid Name, breaks size and/or character restrictions\""};
|
||||
}
|
||||
std::shared_lock<std::shared_mutex> lock{save_lock};
|
||||
if (users.try_emplace_l(
|
||||
|
|
@ -232,11 +236,11 @@ BankResponse Bank::AddUser(std::string &&name, uint32_t init_bal, std::string &&
|
|||
save_flag = true;
|
||||
#endif
|
||||
#endif
|
||||
return {k200OK, "User added!"};
|
||||
return {k204NoContent, std::nullopt};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {k409Conflict, "User already exists"};
|
||||
return {k409Conflict, "\"User already exists\""};
|
||||
}
|
||||
}
|
||||
BankResponse Bank::DelUser(const std::string &name) noexcept
|
||||
|
|
@ -244,11 +248,10 @@ BankResponse Bank::DelUser(const std::string &name) noexcept
|
|||
std::shared_lock<std::shared_mutex> lock{save_lock};
|
||||
#if RETURN_ON_DEL
|
||||
uint32_t bal;
|
||||
if (users.if_contains(name, [this, &bal](const User &u) {
|
||||
bal = u.balance;
|
||||
}))
|
||||
if (users.if_contains(name, [&bal](const User &u) { bal = u.balance; }) &&
|
||||
bal)
|
||||
{
|
||||
users.modify_if(return_account, [ this, bal ](User & u))
|
||||
users.modify_if(return_account, [bal](User & u))
|
||||
{
|
||||
u.balance += bal;
|
||||
}
|
||||
|
|
@ -263,20 +266,20 @@ BankResponse Bank::DelUser(const std::string &name) noexcept
|
|||
save_flag = true;
|
||||
#endif
|
||||
#endif
|
||||
return BankResponse(k200OK, "User deleted!");
|
||||
return {k204NoContent, std::nullopt};
|
||||
}
|
||||
else
|
||||
{
|
||||
return BankResponse(k404NotFound, "User not found");
|
||||
return {k404NotFound, "\"User not found\""};
|
||||
}
|
||||
}
|
||||
void Bank::Save()
|
||||
const char *Bank::Save()
|
||||
{
|
||||
#if CONSERVATIVE_DISK_SAVE
|
||||
if (GetChangeState())
|
||||
{
|
||||
#endif
|
||||
Json::Value temp;
|
||||
static thread_local Json::Value temp;
|
||||
|
||||
//loading info into json temp
|
||||
{
|
||||
|
|
@ -284,7 +287,7 @@ void Bank::Save()
|
|||
for (const auto &u : users)
|
||||
{
|
||||
//we know it contains this key but we call this func to grab mutex
|
||||
users.if_contains(u.first, [&temp, &u](const User &u_val) { temp[u.first.data()] = u_val.Serialize(); });
|
||||
users.if_contains(u.first, [&u](const User &u_val) { temp[u.first] = u_val.Serialize(); });
|
||||
}
|
||||
}
|
||||
if (temp.isNull())
|
||||
|
|
@ -293,9 +296,9 @@ void Bank::Save()
|
|||
}
|
||||
else
|
||||
{
|
||||
std::ofstream user_save(users_location);
|
||||
Json::StreamWriterBuilder builder;
|
||||
const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
|
||||
static thread_local std::ofstream user_save(users_location);
|
||||
static thread_local Json::StreamWriterBuilder builder;
|
||||
static thread_local const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
|
||||
writer->write(temp, &user_save);
|
||||
user_save.close();
|
||||
}
|
||||
|
|
@ -305,6 +308,11 @@ void Bank::Save()
|
|||
#else
|
||||
save_flag = true;
|
||||
#endif
|
||||
return " to disk...\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
return " no changes...\n";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -312,12 +320,12 @@ void Bank::Save()
|
|||
//NOT THREAD SAFE, BY NO MEANS SHOULD THIS BE CALLED WHILE RECEIEVING REQUESTS
|
||||
void Bank::Load()
|
||||
{
|
||||
Json::CharReaderBuilder builder;
|
||||
static thread_local Json::CharReaderBuilder builder;
|
||||
|
||||
Json::Value temp;
|
||||
std::ifstream user_save(users_location);
|
||||
static thread_local Json::Value temp;
|
||||
static thread_local std::ifstream user_save(users_location);
|
||||
builder["collectComments"] = true;
|
||||
JSONCPP_STRING errs;
|
||||
static thread_local JSONCPP_STRING errs;
|
||||
if (!parseFromStream(builder, user_save, &temp, &errs))
|
||||
{
|
||||
std::cerr << errs << '\n';
|
||||
|
|
|
|||
246
src/bank_api.cpp
246
src/bank_api.cpp
|
|
@ -1,54 +1,37 @@
|
|||
#include "bank_api.h"
|
||||
|
||||
// const auto data(R);
|
||||
// auto res = std::make_shared<HttpResponseImpl>(data.first, CT_APPLICATION_JSON);
|
||||
// res->setJsonObject(JsonCast(std::move(data.second)));
|
||||
// doResponseCreateAdvices(res);
|
||||
// callback(res);
|
||||
//all my homies hate jsoncpp
|
||||
|
||||
#define CACHE_FOREVER resp->setExpiredTime(0);
|
||||
#define CACHE_FOREVER resp->setExpiredTime(0)
|
||||
|
||||
#define GEN_BODY \
|
||||
const auto temp_req = req->getJsonObject(); \
|
||||
const auto body = temp_req ? *temp_req : Json::Value();
|
||||
#define CORS resp->addHeader("Access-Control-Allow-Origin", "*")
|
||||
|
||||
#define RESPONSE_PARSE(R) \
|
||||
const auto r(R); \
|
||||
auto resp = HttpResponse::newHttpJsonResponse(JsonCast(std::move(r.second))); \
|
||||
resp->setStatusCode(r.first); \
|
||||
callback(resp);
|
||||
#define GEN_BODY \
|
||||
static thread_local const auto temp_req = req->getJsonObject(); \
|
||||
static thread_local const auto body = temp_req ? *temp_req : Json::Value()
|
||||
|
||||
#define RESPOND_TRUE \
|
||||
auto resp = HttpResponse::newHttpJsonResponse(JsonCast(true)); \
|
||||
CACHE_FOREVER \
|
||||
callback(resp);
|
||||
static thread_local ondemand::parser parser;
|
||||
#define SIMD_JSON_GEN \
|
||||
static thread_local simdjson::padded_string input(req->getBody()); \
|
||||
static thread_local ondemand::document doc = parser.iterate(input)
|
||||
|
||||
#define NAME_PARAM req->getBody().data()
|
||||
#define RESPONSE_PARSE(R) \
|
||||
static thread_local auto resp = HttpResponse::newCustomHttpResponse(R); \
|
||||
CORS; \
|
||||
callback(resp)
|
||||
|
||||
template <typename T>
|
||||
constexpr Json::Value JsonCast(T &&val)
|
||||
#define RESPOND_TRUE \
|
||||
static thread_local auto resp = HttpResponse::newCustomHttpResponse(BankResponse(k204NoContent, std::nullopt)); \
|
||||
CORS; \
|
||||
CACHE_FOREVER; \
|
||||
callback(resp)
|
||||
|
||||
#define NAME_PARAM req->getParameter("name")
|
||||
|
||||
api::api(Bank &b) noexcept : bank(b)
|
||||
{
|
||||
|
||||
if constexpr (std::is_same_v<T, int_fast8_t>)
|
||||
{
|
||||
return (int)val; //becuase of json lib interpreting 67 as 'A' for example
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, uint64_t>)
|
||||
{
|
||||
return (Json::UInt64)val;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, uint32_t>)
|
||||
{
|
||||
return (Json::UInt)val;
|
||||
}
|
||||
else
|
||||
{
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
api::api(Bank &b) noexcept : bank(b) {}
|
||||
|
||||
#if API_VERSION >= 1
|
||||
|
||||
//Usage
|
||||
|
|
@ -56,7 +39,7 @@ void api::GetBal(req_args, const std::string &name) const
|
|||
{
|
||||
RESPONSE_PARSE(bank.GetBal(name));
|
||||
}
|
||||
void api::GetLog(req_args)
|
||||
void api::GetLogs(req_args)
|
||||
{
|
||||
if constexpr (MAX_LOG_SIZE > 0)
|
||||
{
|
||||
|
|
@ -64,99 +47,196 @@ void api::GetLog(req_args)
|
|||
}
|
||||
else
|
||||
{
|
||||
auto resp = HttpResponse::newHttpJsonResponse("Logs are Disabled");
|
||||
resp->setStatusCode(k404NotFound);
|
||||
CACHE_FOREVER
|
||||
static thread_local auto resp = HttpResponse::newCustomHttpResponse(BankResponse(k404NotFound, "\"Logs are Disabled\""));
|
||||
CORS;
|
||||
CACHE_FOREVER;
|
||||
callback(resp);
|
||||
}
|
||||
}
|
||||
void api::SendFunds(req_args) const
|
||||
{
|
||||
GEN_BODY
|
||||
RESPONSE_PARSE(bank.SendFunds(NAME_PARAM, body["to"].asCString(), body["amount"].asUInt()));
|
||||
SIMD_JSON_GEN;
|
||||
auto name = doc.find_field("name").get_string();
|
||||
auto amount = doc.find_field("amount").get_uint64();
|
||||
BankResponse res;
|
||||
if (name.error() || amount.error())
|
||||
{
|
||||
res = BankResponse(k400BadRequest, "Invalid JSON");
|
||||
}
|
||||
else
|
||||
{
|
||||
StrFromSV_Wrapper name_val(name.value());
|
||||
res = bank.SendFunds(NAME_PARAM, name_val.str, amount.value());
|
||||
}
|
||||
RESPONSE_PARSE(std::move(res));
|
||||
}
|
||||
void api::VerifyPassword(req_args) const { RESPOND_TRUE }
|
||||
void api::VerifyPassword(req_args) const { RESPOND_TRUE; }
|
||||
|
||||
//Meta Usage
|
||||
void api::ChangePassword(req_args) const
|
||||
{
|
||||
GEN_BODY
|
||||
bank.ChangePassword(NAME_PARAM, std::move(body["new_pass"].asCString()));
|
||||
RESPOND_TRUE
|
||||
SIMD_JSON_GEN;
|
||||
auto pass = doc.find_field("pass").get_string();
|
||||
BankResponse res;
|
||||
if (pass.error())
|
||||
{
|
||||
res = BankResponse(k400BadRequest, "Invalid JSON");
|
||||
}
|
||||
else
|
||||
{
|
||||
StrFromSV_Wrapper pass_val(pass.value());
|
||||
bank.ChangePassword(NAME_PARAM, std::move(pass_val.str));
|
||||
}
|
||||
RESPOND_TRUE;
|
||||
}
|
||||
void api::AdminChangePassword(req_args) const
|
||||
{
|
||||
GEN_BODY
|
||||
bank.ChangePassword(body["name"].asCString(), std::move(body["new_pass"].asCString()));
|
||||
RESPOND_TRUE
|
||||
SIMD_JSON_GEN;
|
||||
auto name = doc.find_field("name").get_string();
|
||||
auto pass = doc.find_field("pass").get_string();
|
||||
BankResponse res;
|
||||
if (name.error() || pass.error())
|
||||
{
|
||||
res = BankResponse(k400BadRequest, "Invalid JSON");
|
||||
}
|
||||
else
|
||||
{
|
||||
StrFromSV_Wrapper name_val(name.value());
|
||||
StrFromSV_Wrapper pass_val(pass.value());
|
||||
bank.ChangePassword(name_val.str, std::move(pass_val.str));
|
||||
}
|
||||
RESPOND_TRUE;
|
||||
}
|
||||
void api::SetBal(req_args) const
|
||||
{
|
||||
GEN_BODY
|
||||
RESPONSE_PARSE(bank.SetBal(body["name"].asCString(), body["amount"].asUInt()));
|
||||
SIMD_JSON_GEN;
|
||||
auto name = doc.find_field("name").get_string();
|
||||
auto amount = doc.find_field("amount").get_uint64();
|
||||
BankResponse res;
|
||||
if (name.error() || amount.error())
|
||||
{
|
||||
res = BankResponse(k400BadRequest, "Invalid JSON");
|
||||
}
|
||||
else
|
||||
{
|
||||
StrFromSV_Wrapper name_val(name.value());
|
||||
res = bank.SetBal(name_val.str, amount.value());
|
||||
}
|
||||
RESPONSE_PARSE(std::move(res));
|
||||
}
|
||||
void api::AddBal(req_args) const
|
||||
void api::ImpactBal(req_args) const
|
||||
{
|
||||
GEN_BODY
|
||||
RESPONSE_PARSE(bank.AddBal(body["name"].asCString(), body["amount"].asUInt()));
|
||||
}
|
||||
void api::SubBal(req_args) const
|
||||
{
|
||||
GEN_BODY
|
||||
RESPONSE_PARSE(bank.AddBal(body["name"].asCString(), body["amount"].asUInt()));
|
||||
SIMD_JSON_GEN;
|
||||
auto name = doc.find_field("name").get_string();
|
||||
auto amount = doc.find_field("amount").get_int64();
|
||||
BankResponse res;
|
||||
if (name.error() || amount.error())
|
||||
{
|
||||
res = BankResponse(k400BadRequest, "Invalid JSON");
|
||||
}
|
||||
else
|
||||
{
|
||||
StrFromSV_Wrapper name_val(name.value());
|
||||
res = bank.ImpactBal(name_val.str, amount.value());
|
||||
}
|
||||
RESPONSE_PARSE(std::move(res));
|
||||
}
|
||||
|
||||
//System Usage
|
||||
void api::Help(req_args) const
|
||||
{
|
||||
auto resp = HttpResponse::newRedirectionResponse("https://github.com/EntireTwix/CCash/blob/Refractor/README.md"); //may make README.md
|
||||
static thread_local auto resp = HttpResponse::newRedirectionResponse("https://github.com/EntireTwix/CCash/blob/README.md");
|
||||
CACHE_FOREVER;
|
||||
callback(resp);
|
||||
}
|
||||
void api::Ping(req_args) const
|
||||
{
|
||||
RESPOND_TRUE
|
||||
}
|
||||
void api::Close(req_args) const
|
||||
{
|
||||
bank.Save();
|
||||
RESPOND_TRUE; //filter handles admin creds
|
||||
app().quit();
|
||||
RESPOND_TRUE //filter handles admin creds
|
||||
}
|
||||
void api::Contains(req_args, const std::string &name) const
|
||||
{
|
||||
auto resp = HttpResponse::newHttpJsonResponse(JsonCast(bank.Contains(name)));
|
||||
callback(resp);
|
||||
RESPONSE_PARSE(BankResponse(k200OK, bank.Contains(name) ? "true" : "false"));
|
||||
}
|
||||
void api::AdminVerifyAccount(req_args) const
|
||||
{
|
||||
RESPOND_TRUE //filter handles admin creds
|
||||
RESPOND_TRUE; //filter handles admin creds
|
||||
}
|
||||
void api::ApiVersion(req_args) const
|
||||
void api::ApiProperties(req_args) const
|
||||
{
|
||||
auto resp = HttpResponse::newHttpJsonResponse(API_VERSION);
|
||||
//yet to be converted to simdjson
|
||||
Json::Value temp;
|
||||
temp["version"] = API_VERSION;
|
||||
temp["max_log"] = MAX_LOG_SIZE;
|
||||
temp["min_name"] = min_name_size;
|
||||
temp["max_name"] = max_name_size;
|
||||
temp["return_on_del"] = RETURN_ON_DEL;
|
||||
if constexpr (RETURN_ON_DEL)
|
||||
{
|
||||
temp["return_on_del_acc"] = return_account;
|
||||
}
|
||||
|
||||
static thread_local auto resp = HttpResponse::newHttpJsonResponse(std::move(temp));
|
||||
CORS;
|
||||
CACHE_FOREVER;
|
||||
callback(resp);
|
||||
}
|
||||
|
||||
void api::AddUser(req_args) const
|
||||
{
|
||||
GEN_BODY
|
||||
RESPONSE_PARSE(bank.AddUser(body["name"].asCString(), 0, body["pass"].asCString()))
|
||||
SIMD_JSON_GEN;
|
||||
auto name = doc.find_field("name").get_string();
|
||||
auto pass = doc.find_field("pass").get_string();
|
||||
BankResponse res;
|
||||
if (name.error() || pass.error())
|
||||
{
|
||||
res = BankResponse(k400BadRequest, "Invalid JSON");
|
||||
}
|
||||
else
|
||||
{
|
||||
StrFromSV_Wrapper name_val(name.value());
|
||||
StrFromSV_Wrapper pass_val(pass.value());
|
||||
res = bank.AddUser(std::move(name_val.str), 0, std::move(pass_val.str));
|
||||
}
|
||||
RESPONSE_PARSE(std::move(res));
|
||||
}
|
||||
void api::AdminAddUser(req_args) const
|
||||
{
|
||||
GEN_BODY
|
||||
RESPONSE_PARSE(bank.AddUser(body["name"].asCString(), body["balance"].asUInt(), body["pass"].asCString()))
|
||||
SIMD_JSON_GEN;
|
||||
auto name = doc.find_field("name").get_string();
|
||||
auto amount = doc.find_field("amount").get_uint64();
|
||||
auto pass = doc.find_field("pass").get_string();
|
||||
BankResponse res;
|
||||
if (name.error() || amount.error() || pass.error())
|
||||
{
|
||||
res = BankResponse(k400BadRequest, "Invalid JSON");
|
||||
}
|
||||
else
|
||||
{
|
||||
StrFromSV_Wrapper name_val(name.value());
|
||||
StrFromSV_Wrapper pass_val(pass.value());
|
||||
res = bank.AddUser(std::move(name_val.str), amount.value(), std::move(pass_val.str));
|
||||
}
|
||||
RESPONSE_PARSE(std::move(res));
|
||||
}
|
||||
void api::DelUser(req_args) const
|
||||
{
|
||||
GEN_BODY
|
||||
RESPONSE_PARSE(bank.DelUser(NAME_PARAM))
|
||||
RESPONSE_PARSE(bank.DelUser(NAME_PARAM));
|
||||
}
|
||||
void api::AdminDelUser(req_args) const
|
||||
{
|
||||
GEN_BODY
|
||||
RESPONSE_PARSE(bank.DelUser(body["name"].asCString()))
|
||||
SIMD_JSON_GEN;
|
||||
auto name = doc.find_field("name").get_string();
|
||||
BankResponse res;
|
||||
if (name.error())
|
||||
{
|
||||
res = BankResponse(k400BadRequest, "Invalid JSON");
|
||||
}
|
||||
else
|
||||
{
|
||||
StrFromSV_Wrapper name_val(name.value());
|
||||
res = bank.DelUser(name_val.str);
|
||||
}
|
||||
RESPONSE_PARSE(std::move(res));
|
||||
}
|
||||
#endif
|
||||
26
src/bank_resp.cpp
Normal file
26
src/bank_resp.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#include "bank_resp.h"
|
||||
|
||||
template <>
|
||||
drogon::HttpResponsePtr drogon::toResponse(BankResponse &&data)
|
||||
{
|
||||
std::shared_ptr<HttpResponseImpl> res;
|
||||
if (data.second)
|
||||
{
|
||||
res = std::make_shared<HttpResponseImpl>(data.first, CT_APPLICATION_JSON);
|
||||
res->setBody(std::move(*data.second));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = std::make_shared<HttpResponseImpl>();
|
||||
res->setStatusCode(data.first);
|
||||
}
|
||||
const auto &advices = HttpAppFrameworkImpl::instance().getResponseCreationAdvices();
|
||||
if (!advices.empty())
|
||||
{
|
||||
for (auto &advice : advices)
|
||||
{
|
||||
advice(res);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
@ -1,21 +1,28 @@
|
|||
#include "change_flag.h"
|
||||
|
||||
ChangeFlag::ChangeFlag() noexcept {}
|
||||
|
||||
ChangeFlag::ChangeFlag(ChangeFlag &&f) noexcept
|
||||
template <bool init>
|
||||
ChangeFlag<init>::ChangeFlag() noexcept {}
|
||||
template <bool init>
|
||||
ChangeFlag<init>::ChangeFlag(ChangeFlag &&f) noexcept
|
||||
{
|
||||
change_flag.store(f.GetChangeState(), std::memory_order_release); //is this safe?
|
||||
change_flag.store(f.GetChangeState(), std::memory_order_release);
|
||||
}
|
||||
|
||||
void ChangeFlag::SetChangesOn() noexcept
|
||||
template <bool init>
|
||||
void ChangeFlag<init>::SetChangesOn() noexcept
|
||||
{
|
||||
return change_flag.store(1, std::memory_order_release);
|
||||
}
|
||||
void ChangeFlag::SetChangesOff() noexcept
|
||||
template <bool init>
|
||||
void ChangeFlag<init>::SetChangesOff() noexcept
|
||||
{
|
||||
return change_flag.store(0, std::memory_order_release);
|
||||
}
|
||||
bool ChangeFlag::GetChangeState() const noexcept
|
||||
template <bool init>
|
||||
bool ChangeFlag<init>::GetChangeState() const noexcept
|
||||
{
|
||||
return change_flag.load(std::memory_order_acquire);
|
||||
}
|
||||
}
|
||||
|
||||
template class ChangeFlag<true>;
|
||||
template class ChangeFlag<false>;
|
||||
|
|
@ -1,23 +1,41 @@
|
|||
#include "json_filter.h"
|
||||
|
||||
JsonFilter::JsonFilter() {}
|
||||
template <bool check_content_type>
|
||||
JsonFilter<check_content_type>::JsonFilter() {}
|
||||
|
||||
__attribute__((always_inline)) inline bool Contains(std::string_view str, const std::string &val) { return str.find(val) != std::string::npos; }
|
||||
|
||||
void JsonFilter::doFilter(const HttpRequestPtr &req,
|
||||
FilterCallback &&fcb,
|
||||
FilterChainCallback &&fccb)
|
||||
__attribute__((always_inline)) inline bool Contains(std::string_view str, const std::string &val)
|
||||
{
|
||||
return str.find(val) != std::string::npos;
|
||||
}
|
||||
|
||||
template <bool check_content_type>
|
||||
void JsonFilter<check_content_type>::doFilter(const HttpRequestPtr &req,
|
||||
FilterCallback &&fcb,
|
||||
FilterChainCallback &&fccb)
|
||||
{
|
||||
std::string_view content_type = req->getHeader("content-type");
|
||||
std::string_view accept_header = req->getHeader("Accept");
|
||||
|
||||
if (content_type == "applications/json" && (Contains(accept_header, "*/*") || Contains(accept_header, "application/json")))
|
||||
if constexpr (check_content_type)
|
||||
{
|
||||
fccb();
|
||||
return;
|
||||
std::string_view content_type = req->getHeader("content-type");
|
||||
if (content_type == "application/json" && (Contains(accept_header, "*/*") || Contains(accept_header, "application/json")))
|
||||
{
|
||||
fccb();
|
||||
return;
|
||||
}
|
||||
const auto &resp = HttpResponse::newCustomHttpResponse(BankResponse(k406NotAcceptable, "\"Client must Accept and have content-type of JSON\""));
|
||||
fcb(resp);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((Contains(accept_header, "*/*") || Contains(accept_header, "application/json")))
|
||||
{
|
||||
fccb();
|
||||
return;
|
||||
}
|
||||
const auto &resp = HttpResponse::newCustomHttpResponse(BankResponse(k406NotAcceptable, "\"Client must Accept JSON\""));
|
||||
fcb(resp);
|
||||
}
|
||||
}
|
||||
|
||||
const auto &resp = HttpResponse::newHttpJsonResponse("Client must Accept JSON");
|
||||
resp->setStatusCode(k406NotAcceptable);
|
||||
fcb(resp);
|
||||
}
|
||||
template class JsonFilter<true>;
|
||||
template class JsonFilter<false>;
|
||||
59
src/log.cpp
59
src/log.cpp
|
|
@ -1,57 +1,50 @@
|
|||
#include "log.h"
|
||||
|
||||
void Log::AddTrans(Transaction &&t) noexcept
|
||||
void Log::AddTrans(const Transaction &t) noexcept
|
||||
{
|
||||
log_flag.SetChangesOn();
|
||||
#if MAX_LOG_SIZE == 1
|
||||
data = std::move(t);
|
||||
data = t;
|
||||
#else
|
||||
if (data.size() == MAX_LOG_SIZE) // If we hit the max size
|
||||
{
|
||||
for (uint32_t i = 1; i < data.size(); i++) // Make room at the back
|
||||
for (size_t i = 1; i < data.size(); i++) // Make room at the back
|
||||
{
|
||||
data[i - 1] = std::move(data[i]); // Shifts everything left
|
||||
}
|
||||
data[data.size() - 1] = std::move(t); // Place new in opened spot
|
||||
return;
|
||||
}
|
||||
else if (data.size() == data.capacity()) // If we haven't hit the max but hit capacity
|
||||
{
|
||||
data.reserve(data.capacity() + PRE_LOG_SIZE); // Reserve more memory
|
||||
}
|
||||
data.push_back(std::move(t)); // In either case we have space under max length, move to new spot
|
||||
data.push_back(t); // In either case we have space under max length, move to new spot
|
||||
#endif
|
||||
log_flag.SetChangesOn();
|
||||
}
|
||||
|
||||
const Json::Value &Log::GetLog() noexcept
|
||||
const std::string &Log::GetLogs() noexcept
|
||||
{
|
||||
if (log_flag.GetChangeState()) //if there are changes
|
||||
{
|
||||
//re-generate snapshot
|
||||
Json::Value res;
|
||||
#if MAX_LOG_SIZE == 1
|
||||
res[0]["to"] = data.to;
|
||||
res[0]["from"] = data.from;
|
||||
#ifdef _USE_32BIT_TIME_T
|
||||
res[0]["time"] = (Json::UInt)data.time;
|
||||
#else
|
||||
res[0]["time"] = (Json::UInt64)data.time;
|
||||
#endif
|
||||
#else
|
||||
for (uint32_t i = data.size(); i > 0; --i)
|
||||
//({\"amount\":1,\"from\":\"\",\"time\":1625943626,\"to\":\"\"}, + (2*max_name_size)+10+10) * # of logs) + 1
|
||||
size_t predicted_size = ((58 + (2 * max_name_size)) * data.size()) + 1;
|
||||
if (log_snapshot.capacity() < predicted_size)
|
||||
{
|
||||
res[i - 1]["to"] = data[data.size() - i].to;
|
||||
res[i - 1]["from"] = data[data.size() - i].from;
|
||||
res[i - 1]["amount"] = (Json::UInt)data[data.size() - i].amount;
|
||||
#ifdef _USE_32BIT_TIME_T
|
||||
res[i - 1]["time"] = (Json::UInt)data[data.size() - i].time;
|
||||
#else
|
||||
res[i - 1]["time"] = (Json::UInt64)data[data.size() - i].time;
|
||||
#endif
|
||||
log_snapshot.reserve(predicted_size);
|
||||
}
|
||||
#endif
|
||||
log_snapshot = '[';
|
||||
for (size_t i = 0; i < data.size(); ++i)
|
||||
{
|
||||
log_snapshot += "{\"to\":\"";
|
||||
log_snapshot += data[i].to;
|
||||
log_snapshot += "\",\"from\":\"";
|
||||
log_snapshot += data[i].from;
|
||||
log_snapshot += "\",\"amount\":";
|
||||
log_snapshot += std::to_string(data[i].amount);
|
||||
log_snapshot += ",\"time\":";
|
||||
log_snapshot += std::to_string(data[i].time);
|
||||
log_snapshot += "},";
|
||||
}
|
||||
log_snapshot[log_snapshot.size() - 1] = ']';
|
||||
log_flag.SetChangesOff();
|
||||
log_snapshot = res;
|
||||
}
|
||||
return log_snapshot;
|
||||
}
|
||||
|
|
@ -74,9 +67,9 @@ Json::Value Log::Serialize() const
|
|||
res[i]["from"] = data[i].from;
|
||||
res[i]["amount"] = (Json::UInt)data[i].amount;
|
||||
#ifdef _USE_32BIT_TIME_T
|
||||
res[i]["time"] = (Json::UInt)data[i].time;
|
||||
res[i]["time"] = (Json::Int)data[i].time;
|
||||
#else
|
||||
res[i]["time"] = (Json::UInt64)data[i].time;
|
||||
res[i]["time"] = (Json::Int64)data[i].time;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
12032
src/simdjson.cpp
Normal file
12032
src/simdjson.cpp
Normal file
File diff suppressed because it is too large
Load diff
45
src/str_intrusion.cpp
Normal file
45
src/str_intrusion.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#include "str_intrusion.h"
|
||||
|
||||
//this function is horribly jank
|
||||
template <typename Tag>
|
||||
struct result
|
||||
{
|
||||
typedef typename Tag::type type;
|
||||
static type ptr;
|
||||
};
|
||||
template <typename Tag>
|
||||
typename result<Tag>::type result<Tag>::ptr;
|
||||
|
||||
template <typename Tag, typename Tag::type p>
|
||||
struct rob : result<Tag>
|
||||
{
|
||||
struct filler
|
||||
{
|
||||
filler() { result<Tag>::ptr = p; }
|
||||
};
|
||||
static filler filler_obj;
|
||||
};
|
||||
template <typename Tag, typename Tag::type p>
|
||||
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;
|
||||
struct string_length
|
||||
{
|
||||
typedef void (std::string::*type)(size_t);
|
||||
};
|
||||
template class rob<string_length, &std::string::_M_length>;
|
||||
struct string_data
|
||||
{
|
||||
typedef void (std::string::*type)(char *);
|
||||
};
|
||||
template class rob<string_data, &std::string::_M_data>;
|
||||
|
||||
StrFromSV_Wrapper::StrFromSV_Wrapper() noexcept {}
|
||||
StrFromSV_Wrapper::StrFromSV_Wrapper(std::string_view sv) noexcept
|
||||
{
|
||||
(str.*result<string_data>::ptr)((char *)sv.data());
|
||||
(str.*result<string_length>::ptr)(sv.size());
|
||||
}
|
||||
StrFromSV_Wrapper::~StrFromSV_Wrapper() noexcept
|
||||
{
|
||||
(str.*result<string_data>::ptr)(nullptr);
|
||||
(str.*result<string_length>::ptr)(0);
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include "transaction.h"
|
||||
|
||||
Transaction::Transaction() = default;
|
||||
Transaction::Transaction(const std::string &from_str, const std::string &to_str, uint32_t amount, time_t time_val) : from(from_str), to(to_str), amount(amount), time(time_val) {}
|
||||
Transaction::Transaction(const std::string &from_str, const std::string &to_str, uint32_t amount) : from(from_str), to(to_str), amount(amount) { time = std::time(NULL); }
|
||||
Transaction::Transaction() noexcept {};
|
||||
Transaction::Transaction(const std::string &from_str, const std::string &to_str, uint32_t amount, time_t time_val) noexcept : from(from_str), to(to_str), amount(amount), time(time_val) {}
|
||||
Transaction::Transaction(const std::string &from_str, const std::string &to_str, uint32_t amount) noexcept : from(from_str), to(to_str), amount(amount) { time = std::time(NULL); }
|
||||
20
src/user.cpp
20
src/user.cpp
|
|
@ -27,17 +27,21 @@ User::User(uint32_t init_bal, XXH64_hash_t init_pass, const Json::Value &log_j)
|
|||
{
|
||||
if (log_j.size())
|
||||
{
|
||||
log.data.reserve(std::min((size_t)PRE_LOG_SIZE * ((log_j.size() / PRE_LOG_SIZE) + 1), (size_t)MAX_LOG_SIZE));
|
||||
for (uint32_t i = (log_j.size() - MAX_LOG_SIZE) * (log_j.size() > MAX_LOG_SIZE); i < log_j.size(); i++)
|
||||
{
|
||||
log.data.push_back(Transaction(
|
||||
log_j[i]["from"].asCString(),
|
||||
log_j[i]["to"].asCString(),
|
||||
log_j[i]["amount"].asUInt(),
|
||||
#ifdef _USE_32BIT_TIME_T
|
||||
log_j[i]["time"].asUInt()));
|
||||
#if MAX_LOG_SIZE == 1
|
||||
log.data = (
|
||||
#else
|
||||
log_j[i]["time"].asUInt64()));
|
||||
log.data.push_back(
|
||||
#endif
|
||||
Transaction(
|
||||
log_j[i]["from"].asCString(),
|
||||
log_j[i]["to"].asCString(),
|
||||
log_j[i]["amount"].asUInt(),
|
||||
#ifdef _USE_32BIT_TIME_T
|
||||
log_j[i]["time"].asUInt()));
|
||||
#else
|
||||
log_j[i]["time"].asUInt64()));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +1,103 @@
|
|||
#include "user_filter.h"
|
||||
|
||||
UserFilter::UserFilter(Bank &b) : bank(b) {}
|
||||
template <bool set_body_flag, bool require_admin>
|
||||
UserFilter<set_body_flag, require_admin>::UserFilter(Bank &b) : bank(b) {}
|
||||
|
||||
void UserFilter::doFilter(const HttpRequestPtr &req,
|
||||
FilterCallback &&fcb,
|
||||
FilterChainCallback &&fccb)
|
||||
#define time_func_a(f, a, x) \
|
||||
{ \
|
||||
using namespace std::chrono; \
|
||||
uint32_t timer = 0; \
|
||||
for (int i = 0; i < x; ++i) \
|
||||
{ \
|
||||
auto t1 = high_resolution_clock::now().time_since_epoch(); \
|
||||
f; \
|
||||
auto t2 = high_resolution_clock::now().time_since_epoch(); \
|
||||
a; \
|
||||
timer += std::chrono::duration_cast<std::chrono::nanoseconds>((t2 - t1)).count(); \
|
||||
} \
|
||||
std::cout << timer / x << '\n'; \
|
||||
}
|
||||
|
||||
#define time_func(f, x) \
|
||||
{ \
|
||||
using namespace std::chrono; \
|
||||
uint32_t timer = 0; \
|
||||
for (int i = 0; i < x; ++i) \
|
||||
{ \
|
||||
auto t1 = high_resolution_clock::now().time_since_epoch(); \
|
||||
f; \
|
||||
auto t2 = high_resolution_clock::now().time_since_epoch(); \
|
||||
timer += std::chrono::duration_cast<std::chrono::nanoseconds>((t2 - t1)).count(); \
|
||||
} \
|
||||
std::cout << timer / x << '\n'; \
|
||||
}
|
||||
|
||||
#define Op_a(v, name, x, a) \
|
||||
{ \
|
||||
std::cout << name; \
|
||||
time_func_a(v, a, x); \
|
||||
}
|
||||
|
||||
#define Op(v, name, x) \
|
||||
{ \
|
||||
std::cout << name; \
|
||||
time_func(v, x); \
|
||||
}
|
||||
|
||||
template <bool set_body_flag, bool require_admin>
|
||||
void UserFilter<set_body_flag, require_admin>::doFilter(const HttpRequestPtr &req,
|
||||
FilterCallback &&fcb,
|
||||
FilterChainCallback &&fccb)
|
||||
{
|
||||
std::string_view auth_header = req->getHeader("Authorization");
|
||||
if (auth_header.size() > 6)
|
||||
if (auth_header.size() > 6 && auth_header.size() <= ((max_name_size + 256) * 4) / 3) //"Basic " + (username + ':' + password) * 4/3
|
||||
{
|
||||
if (auth_header.substr(0, 6) == "Basic ")
|
||||
{
|
||||
std::string_view base64_input = auth_header.substr(6);
|
||||
char base64_result[(base64_input.size() * 3) / 4]; //only alloc
|
||||
char result_buffer[max_name_size + 256]; //(username + ':' + 255 password)
|
||||
size_t new_sz;
|
||||
base64_decode(base64_input.data(), base64_input.size(), base64_result, &new_sz, 0);
|
||||
base64_decode(base64_input.data(), base64_input.size(), result_buffer, &new_sz, 0);
|
||||
|
||||
std::string_view results_view(base64_result, new_sz);
|
||||
std::string_view results_view(result_buffer, new_sz);
|
||||
std::size_t middle = results_view.find(':');
|
||||
if (middle != std::string::npos)
|
||||
if (middle != std::string::npos && ((new_sz - middle) <= 256))
|
||||
{
|
||||
std::string_view username = results_view.substr(0, middle);
|
||||
std::string_view password = results_view.substr(middle + 1);
|
||||
if (bank.VerifyPassword(username, password))
|
||||
StrFromSV_Wrapper username(results_view.substr(0, middle));
|
||||
if (ValidUsername(username.str)) //check if username is a valid attempt to avoid hashing/grabbing shared lock
|
||||
{
|
||||
fccb();
|
||||
return;
|
||||
if constexpr (require_admin)
|
||||
{
|
||||
if (bank.AdminVerifyAccount(username.str))
|
||||
{
|
||||
StrFromSV_Wrapper password(results_view.substr(middle + 1));
|
||||
if (bank.VerifyPassword(username.str, password.str))
|
||||
{
|
||||
fccb();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StrFromSV_Wrapper password(results_view.substr(middle + 1));
|
||||
if (bank.VerifyPassword(username.str, results_view.substr(middle + 1)))
|
||||
{
|
||||
if constexpr (set_body_flag)
|
||||
{
|
||||
req->setParameter("name", username.str);
|
||||
}
|
||||
fccb();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto &resp = HttpResponse::newHttpJsonResponse("Invalid Credentials");
|
||||
resp->setStatusCode(k401Unauthorized);
|
||||
fcb(resp);
|
||||
}
|
||||
fcb(HttpResponse::newCustomHttpResponse(BankResponse(k401Unauthorized, "\"Invalid Credentials\"")));
|
||||
}
|
||||
|
||||
template class UserFilter<true, false>; //user default
|
||||
template class UserFilter<false, false>; //user sparse
|
||||
template class UserFilter<false, true>; //admin
|
||||
42
src/xxhash.c
Normal file
42
src/xxhash.c
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* xxHash - Extremely Fast Hash algorithm
|
||||
* Copyright (C) 2012-2020 Yann Collet
|
||||
*
|
||||
* BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You can contact the author at:
|
||||
* - xxHash homepage: https://www.xxhash.com
|
||||
* - xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
*/
|
||||
|
||||
/*
|
||||
* xxhash.c instantiates functions defined in xxhash.h
|
||||
*/
|
||||
|
||||
#define XXH_STATIC_LINKING_ONLY /* access advanced declarations */
|
||||
#define XXH_IMPLEMENTATION /* access definitions */
|
||||
|
||||
#include "xxhash.h"
|
||||
|
|
@ -4,11 +4,7 @@ XXH64_hash_t xxHashStringGen::operator()(const std::string &str) const noexcept
|
|||
{
|
||||
return XXH3_64bits(str.data(), str.size());
|
||||
}
|
||||
XXH64_hash_t xxHashStringGen::operator()(std::string &&str) const noexcept
|
||||
XXH64_hash_t xxHashStringGen::operator()(const std::string_view &str) const noexcept
|
||||
{
|
||||
return XXH3_64bits(str.data(), str.size());
|
||||
}
|
||||
XXH64_hash_t xxHashStringGen::operator()(std::string_view str) const noexcept
|
||||
{
|
||||
return XXH3_64bits(str.data(), str.size());
|
||||
}
|
||||
}
|
||||
1
third_party/xxHash
vendored
1
third_party/xxHash
vendored
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 0e49217046917180b9118a2d89aceaa76a65cf51
|
||||
Loading…
Reference in a new issue