refactor gradido transaction handling

This commit is contained in:
Dario 2020-10-14 17:33:37 +02:00 committed by Ulf Gebhardt
parent c8b9e3b0f5
commit 5e524d7ac6
No known key found for this signature in database
GPG Key ID: 81308EFE29ABFEBD
34 changed files with 770 additions and 40 deletions

View File

@ -8,6 +8,7 @@
#include "SingletonManager/ConnectionManager.h"
#include "SingletonManager/SessionManager.h"
#include "SingletonManager/EmailManager.h"
#include "SingletonManager/PendingTasksManager.h"
#include "controller/User.h"
@ -252,6 +253,9 @@ int Gradido_LoginServer::main(const std::vector<std::string>& args)
// start the json server
json_srv.start();
// load pending tasks not finished in last session
PendingTasksManager::getInstance()->load();
printf("[Gradido_LoginServer::main] started in %s\n", usedTime.string().data());
// wait for CTRL-C or kill
waitForTerminationRequest();

View File

@ -52,6 +52,14 @@ int MemoryBin::convertFromHex(const std::string& hex)
return 0;
}
bool MemoryBin::isSame(const MemoryBin* b) const
{
if (b->size() != size()) {
return false;
}
return 0 == memcmp(data(), b->data(), size());
}
// *************************************************************

View File

@ -50,6 +50,8 @@ public:
//! -2 if hex is invalid
int convertFromHex(const std::string& hex);
bool isSame(const MemoryBin* b) const;
protected:
MemoryBin(Poco::UInt32 size);
~MemoryBin();

View File

@ -0,0 +1,67 @@
#include "PendingTasksManager.h"
PendingTasksManager::PendingTasksManager()
{
}
PendingTasksManager::~PendingTasksManager()
{
Poco::ScopedLock<Poco::Mutex> _lock(mWorkMutex);
for (auto it = mPendingTasks.begin(); it != mPendingTasks.end(); it++) {
delete it->second;
}
mPendingTasks.clear();
}
PendingTasksManager* PendingTasksManager::getInstance()
{
static PendingTasksManager theOne;
return &theOne;
}
int PendingTasksManager::load()
{
auto pending_tasks = controller::PendingTask::loadAll();
for (auto it = pending_tasks.begin(); it != pending_tasks.end(); it++) {
addTask(*it);
}
return 0;
}
int PendingTasksManager::addTask(Poco::AutoPtr<controller::PendingTask> task)
{
if (task.isNull() || !task->getModel()) {
return -1;
}
auto model = task->getModel();
Poco::ScopedLock<Poco::Mutex> _lock(mWorkMutex);
auto pending_task_list = getTaskListForUser(model->getUserId());
pending_task_list->push_back(task);
return 0;
}
PendingTasksManager::PendingTaskList* PendingTasksManager::getTaskListForUser(int userId)
{
Poco::ScopedLock<Poco::Mutex> _lock(mWorkMutex);
auto it = mPendingTasks.find(userId);
if (it == mPendingTasks.end()) {
auto pending_list = new PendingTaskList;
mPendingTasks.insert(std::pair<int, PendingTaskList*>(userId, pending_list));
return pending_list;
}
else {
return it->second;
}
}
const PendingTasksManager::PendingTaskList* PendingTasksManager::getTaskListForUser(int userId) const
{
Poco::ScopedLock<Poco::Mutex> _lock(mWorkMutex);
auto it = mPendingTasks.find(userId);
if (it != mPendingTasks.end()) {
return it->second;
}
return nullptr;
}

View File

@ -0,0 +1,50 @@
/*!
*
* \author: einhornimmond
*
* \date: 13.10.20
*
* \brief: manage tasks which need to wait on extern work
* like hedera tasks waiting on hedera network processing transactions
* like gradido transactions which are signed from additional people like ManageGroup Tasks
*/
#ifndef GRADIDO_LOGIN_SERVER_SINGLETON_MANAGER_PENDING_TASKS_MANAGER
#define GRADIDO_LOGIN_SERVER_SINGLETON_MANAGER_PENDING_TASKS_MANAGER
#include "../controller/PendingTask.h"
class PendingTasksManager: public UniLib::lib::MultithreadContainer
{
public:
typedef std::list<Poco::AutoPtr<controller::PendingTask>> PendingTaskList;
~PendingTasksManager();
static PendingTasksManager* getInstance();
//! \brief load pending tasks from db at server start
int load();
//! \return -1 task is zero
//! \return 0 if added
int addTask(Poco::AutoPtr<controller::PendingTask> task);
//! by calling this, important is to call lock to prevent vanishing the list while working with it,
//! and unlock afterwards
//! \return list or nullptr if no list for user exist
const PendingTaskList* getTaskListForUser(int userId) const;
protected:
PendingTasksManager();
std::map<int, PendingTaskList*> mPendingTasks;
//! \return list for user, creating new list and map entry if not exist
PendingTaskList* getTaskListForUser(int userId);
};
#endif //GRADIDO_LOGIN_SERVER_SINGLETON_MANAGER_PENDING_TASKS_MANAGER

View File

@ -1,5 +1,8 @@
#include "PendingTask.h"
#include "../tasks/GradidoGroupAddMemberTask.h"
namespace controller {
PendingTask::PendingTask(model::table::PendingTask* dbModel)
@ -15,7 +18,7 @@ namespace controller {
Poco::AutoPtr<PendingTask> PendingTask::create(int userId, std::string serializedProtoRequest, model::table::TaskType type)
{
auto db = new model::table::PendingTask(userId, serializedProtoRequest, type);
auto pending_task = new PendingTask(db);
auto pending_task = loadCorrectDerivedClass(db);
return Poco::AutoPtr<PendingTask>(pending_task);
}
@ -26,11 +29,41 @@ namespace controller {
std::vector<Poco::AutoPtr<PendingTask>> resultVector;
resultVector.reserve(pending_task_list.size());
for (auto it = pending_task_list.begin(); it != pending_task_list.end(); it++) {
resultVector.push_back(new PendingTask(new model::table::PendingTask(*it)));
resultVector.push_back(loadCorrectDerivedClass(new model::table::PendingTask(*it)));
}
return resultVector;
}
std::vector<Poco::AutoPtr<PendingTask>> PendingTask::loadAll()
{
auto db = new model::table::PendingTask();
std::vector<model::table::PendingTaskTuple> task_list;
// throw an unresolved external symbol error
task_list = db->loadAllFromDB<model::table::PendingTaskTuple>();
//*/ //work around end
std::vector<Poco::AutoPtr<PendingTask>> resultVector;
resultVector.reserve(task_list.size());
for (auto it = task_list.begin(); it != task_list.end(); it++) {
auto group_ptr = loadCorrectDerivedClass(new model::table::PendingTask(*it));
resultVector.push_back(group_ptr);
}
return resultVector;
}
Poco::AutoPtr<PendingTask> PendingTask::loadCorrectDerivedClass(model::table::PendingTask* dbModel)
{
if (!dbModel) return nullptr;
auto type = dbModel->getTaskType();
switch (type) {
case model::table::TASK_TYPE_GROUP_ADD_MEMBER: return new GradidoGroupAddMemberTask(dbModel);
default: return nullptr;
}
return nullptr;
}
}

View File

@ -21,13 +21,20 @@ namespace controller {
static Poco::AutoPtr<PendingTask> create(int userId, std::string serializedProtoRequest, model::table::TaskType type);
static std::vector<Poco::AutoPtr<PendingTask>> load(int userId);
static std::vector<Poco::AutoPtr<PendingTask>> loadAll();
inline bool deleteFromDB() { return mDBModel->deleteFromDB(); }
inline Poco::AutoPtr<model::table::PendingTask> getModel() { return _getModel<model::table::PendingTask>(); }
virtual bool isTimeoutTask() = 0;
virtual Poco::DateTime getNextRunTime() { return Poco::DateTime(); };
//! \return 1 run finished, more runs needed
//! \return 0 run finished, no more runs needed
//! \return -1 error, more runs needed
//! \return -2 critical error, abort, remove
virtual int run() { return false; };
protected:
static Poco::AutoPtr<PendingTask> loadCorrectDerivedClass(model::table::PendingTask* dbModel);
PendingTask(model::table::PendingTask* dbModel);

View File

@ -277,6 +277,13 @@ namespace DataTypeConverter
return microseconds;
}
Poco::Timestamp convertFromProtoTimestampSeconds(const proto::gradido::TimestampSeconds& timestampSeconds)
{
google::protobuf::int64 microseconds = timestampSeconds.seconds() * (google::protobuf::int64)10e5;
return microseconds;
}
Poco::Timespan convertFromProtoDuration(const proto::Duration& duration)
{
return Poco::Timespan(duration.seconds(), 0);

View File

@ -11,6 +11,7 @@
#include "../proto/hedera/Timestamp.pb.h"
#include "../proto/hedera/Duration.pb.h"
#include "../proto/gradido/BasicTypes.pb.h"
#include "sodium.h"
@ -50,6 +51,7 @@ namespace DataTypeConverter {
std::string convertTimespanToLocalizedString(Poco::Timespan duration, LanguageCatalog* lang);
Poco::Timestamp convertFromProtoTimestamp(const proto::Timestamp& timestamp);
Poco::Timestamp convertFromProtoTimestampSeconds(const proto::gradido::TimestampSeconds& timestampSeconds);
Poco::Timespan convertFromProtoDuration(const proto::Duration& duration);
};

View File

@ -18,15 +18,59 @@ namespace model {
int GroupMemberUpdate::prepare()
{
const static char functionName[] = { "GroupMemberUpdate::prepare" };
auto target_group = mProtoMemberUpdate.target_group();
auto sm = SessionManager::getInstance();
auto mm = MemoryManager::getInstance();
if (mProtoMemberUpdate.user_pubkey().size() != KeyPairEd25519::getPublicKeySize()) {
return -1;
}
auto pubkey_copy = mm->getFreeMemory(KeyPairEd25519::getPublicKeySize());
memcpy(*pubkey_copy, mProtoMemberUpdate.user_pubkey().data(), KeyPairEd25519::getPublicKeySize());
mRequiredSignPublicKeys.push_back(pubkey_copy);
if (sm->isValid(target_group, VALIDATE_GROUP_ALIAS)) {
auto groups = controller::Group::load(mProtoMemberUpdate.target_group());
if (groups.size() > 0 && !groups[0].isNull() && groups[0]->getModel()) {
auto user_db = controller::User::create();
auto count = user_db->getModel()->countColumns("group_id", groups[0]->getModel()->getID());
if (!count)
{
// no current user in group, at least login server known, so we need only one signature for transaction
// TODO: maybe check with node server, but maybe it isn't necessary
mMinSignatureCount = 1;
}
else
{
// at least one more user is in group
// now we need the voting system to decide how many and which signatures are needed
// for current version we need only one another
mMinSignatureCount = 2;
}
}
/*if (groups.size() != 1) {
addError(new ParamError(functionName, "target group not known or not unambiguous: ", target_group));
return TRANSACTION_VALID_INVALID_GROUP_ALIAS;
}*/
}
return 0;
}
TransactionValidation GroupMemberUpdate::validate()
{
const static char functionName[] = { "GroupMemberUpdate::validate" };
if (mProtoMemberUpdate.user_pubkey().size() != KeyPairEd25519::getPublicKeySize()) {
addError(new Error(functionName, "pubkey not set or wrong size"));
return -1;
return TRANSCATION_VALID_INVALID_PUBKEY;
}
if (mProtoMemberUpdate.member_update_type() != proto::gradido::GroupMemberUpdate::ADD_USER) {
addError(new Error(functionName, "user move not implemented yet!"));
return 1;
return TRANSACTION_VALID_CODE_ERROR;
}
auto target_group = mProtoMemberUpdate.target_group();
auto sm = SessionManager::getInstance();
@ -34,15 +78,21 @@ namespace model {
auto groups = controller::Group::load(mProtoMemberUpdate.target_group());
if (groups.size() != 1) {
addError(new ParamError(functionName, "target group not known or not unambiguous: ", target_group));
return -2;
return TRANSACTION_VALID_INVALID_GROUP_ALIAS;
}
}
else {
addError(new Error(functionName, "target group isn't valid group alias string "));
return -3;
return TRANSACTION_VALID_INVALID_GROUP_ALIAS;
}
return 0;
return TRANSACTION_VALID_OK;
}
/*
GroupMemberUpdate::GroupMemberUpdate(const std::string& memo, Poco::AutoPtr<controller::User> user, Poco::AutoPtr<controller::Group> group)
{
}
*/
}
}

View File

@ -4,6 +4,7 @@
#include "TransactionBase.h"
#include "../../proto/gradido/GroupMemberUpdate.pb.h"
namespace model {
namespace gradido {
class GroupMemberUpdate : public TransactionBase
@ -12,6 +13,7 @@ namespace model {
GroupMemberUpdate(const std::string& memo, const proto::gradido::GroupMemberUpdate &protoGroupMemberUpdate);
~GroupMemberUpdate();
int prepare();
TransactionValidation validate();
protected:
const proto::gradido::GroupMemberUpdate& mProtoMemberUpdate;

View File

@ -0,0 +1,123 @@
#include "Transaction.h"
#include "../../SingletonManager/ErrorManager.h"
namespace model {
namespace gradido {
Transaction::Transaction(Poco::AutoPtr<TransactionBody> body)
: mTransactionBody(body), mBodyBytesHash(0)
{
}
Transaction::~Transaction()
{
}
bool Transaction::addSign(Poco::AutoPtr<controller::User> user)
{
static const char function_name[] = "Transaction::addSign";
if (user.isNull() || !user->getModel())
{
addError(new Error(function_name, "error user is invalid"));
return false;
}
std::string bodyBytes;
try {
bodyBytes = mTransactionBody->getBodyBytes();
}
catch (Poco::Exception& ex) {
addError(new Error(function_name, "error getting body bytes"));
return false;
}
auto hash = DRMakeStringHash(bodyBytes.data(), bodyBytes.size());
auto sigMap = mProtoTransaction.mutable_sig_map();
if (sigMap->sigpair_size() > 0 && mBodyBytesHash && mBodyBytesHash != hash)
{
addError(new Error(function_name, "body bytes hash has changed and signature(s) exist already!"));
return false;
}
mBodyBytesHash = hash;
auto pubkeyBin = user->getModel()->getPublicKey();
// check if pubkey already exist
for (auto it = sigMap->sigpair().begin(); it != sigMap->sigpair().end(); it++)
{
if (it->pubkey().size() != KeyPairEd25519::getPublicKeySize()) {
addError(new Error(function_name, "error signature pubkey hasn't expected size!"));
return false;
}
if (0 == memcmp(pubkeyBin, it->pubkey().data(), KeyPairEd25519::getPublicKeySize())) {
addError(new ParamError(function_name, "error, pubkey has signed already from user: ", user->getModel()->getEmail()));
return false;
}
}
auto mm = MemoryManager::getInstance();
auto gradido_key_pair = user->getGradidoKeyPair();
KeyPairEd25519* recovered_gradido_key_pair = nullptr;
if (!gradido_key_pair || !gradido_key_pair->hasPrivateKey())
{
if (!user->tryLoadPassphraseUserBackup(&recovered_gradido_key_pair))
{
if (user->setGradidoKeyPair(recovered_gradido_key_pair))
{
user->getModel()->updatePrivkey();
}
}
else
{
addError(new Error(function_name, "user cannot decrypt private key"));
return false;
}
}
MemoryBin* sign = nullptr;
if (gradido_key_pair)
{
sign = gradido_key_pair->sign(bodyBytes);
}
else if (recovered_gradido_key_pair)
{
sign = recovered_gradido_key_pair->sign(bodyBytes);
}
if (!sign)
{
ErrorManager::getInstance()->sendErrorsAsEmail();
addError(new Error(function_name, "error by calculate signature"));
mm->releaseMemory(sign);
return false;
}
auto sigPair = sigMap->add_sigpair();
auto pubkeyBytes = sigPair->mutable_pubkey();
*pubkeyBytes = std::string((const char*)pubkeyBin, crypto_sign_PUBLICKEYBYTES);
auto sigBytes = sigPair->mutable_ed25519();
*sigBytes = std::string((char*)*sign, sign->size());
mm->releaseMemory(sign);
return true;
}
TransactionValidation Transaction::validate()
{
auto sig_map = mProtoTransaction.sig_map();
auto transaction_base = mTransactionBody->getTransactionBase();
auto result = transaction_base->checkRequiredSignatures(&sig_map);
if (result == TRANSACTION_VALID_OK)
{
return transaction_base->validate();
}
return result;
}
}
}

View File

@ -0,0 +1,37 @@
#ifndef GRADIDO_LOGIN_SERVER_MODEL_GRADIDO_TRANSACTION_H
#define GRADIDO_LOGIN_SERVER_MODEL_GRADIDO_TRANSACTION_H
/*
* @author: Dario Rekowski
*
* @date: 12.10.2020
*
* @brief: mainly for signing gradido transaction
*/
#include "../../proto/gradido/GradidoTransaction.pb.h"
#include "TransactionBody.h"
#include "../../controller/User.h"
namespace model {
namespace gradido {
class Transaction : public NotificationList
{
public:
Transaction(Poco::AutoPtr<TransactionBody> body);
~Transaction();
bool addSign(Poco::AutoPtr<controller::User> user);
TransactionValidation validate();
protected:
Poco::AutoPtr<TransactionBody> mTransactionBody;
proto::gradido::GradidoTransaction mProtoTransaction;
HASH mBodyBytesHash;
};
}
}
#endif //GRADIDO_LOGIN_SERVER_MODEL_GRADIDO_TRANSACTION_H

View File

@ -1,15 +1,32 @@
#include "TransactionBase.h"
#include "../../Crypto/KeyPairEd25519.h"
#include <iomanip>
namespace model {
namespace gradido {
TransactionBase::TransactionBase(const std::string& memo)
: mMemo(memo)
: mMemo(memo), mMinSignatureCount(0)
{
}
TransactionBase::~TransactionBase()
{
auto mm = MemoryManager::getInstance();
for (auto it = mRequiredSignPublicKeys.begin(); it != mRequiredSignPublicKeys.end(); it++)
{
mm->releaseMemory(*it);
}
mRequiredSignPublicKeys.clear();
for (auto it = mForbiddenSignPublicKeys.begin(); it != mForbiddenSignPublicKeys.end(); it++)
{
mm->releaseMemory(*it);
}
mForbiddenSignPublicKeys.clear();
}
std::string TransactionBase::amountToString(google::protobuf::int64 amount)
{
std::stringstream ss;
@ -25,6 +42,77 @@ namespace model {
return amountString;
//return ss.str();
}
TransactionValidation TransactionBase::checkRequiredSignatures(const proto::gradido::SignatureMap* sig_map)
{
if (!mMinSignatureCount) {
addError(new Error("TransactionBase::checkRequiredSignatures", "mMinSignatureCount is zero"));
return TRANSACTION_VALID_CODE_ERROR;
}
// not enough
if (mMinSignatureCount > sig_map->sigpair_size()) {
return TRANSACTION_VALID_MISSING_SIGN;
}
// enough
if (!mRequiredSignPublicKeys.size() && !mForbiddenSignPublicKeys.size()) {
return TRANSACTION_VALID_OK;
}
// check if specific signatures can be found
static const char function_name[] = "TransactionBase::checkRequiredSignatures";
// prepare
std::vector<MemoryBin*> required_keys = mRequiredSignPublicKeys;
bool forbidden_key_found = false;
for (auto it = sig_map->sigpair().begin(); it != sig_map->sigpair().end(); it++)
{
auto pubkey_size = it->pubkey().size();
if (pubkey_size != KeyPairEd25519::getPublicKeySize())
{
addError(new ParamError(function_name, "signature pubkey size is not as expected: ", pubkey_size));
return TRANSACTION_VALID_CODE_ERROR;
}
// check for forbidden key
if (!forbidden_key_found && mForbiddenSignPublicKeys.size())
{
for (auto it2 = mForbiddenSignPublicKeys.begin(); it2 != mForbiddenSignPublicKeys.end(); it2++) {
if ((*it2)->size() != KeyPairEd25519::getPublicKeySize())
{
addError(new ParamError(function_name, "forbidden sign public key size is not as expected: ", (*it2)->size()));
return TRANSACTION_VALID_CODE_ERROR;
}
if (0 == memcmp((*it2)->data(), it->pubkey().data(), pubkey_size))
{
forbidden_key_found = true;
break;
}
}
}
if (forbidden_key_found) break;
// compare with required keys
for (auto it3 = required_keys.begin(); it3 != required_keys.end(); it3++)
{
if ((*it3)->size() != KeyPairEd25519::getPublicKeySize())
{
addError(new ParamError(function_name, "required sign public key size is not as expected: ", (*it3)->size()));
return TRANSACTION_VALID_CODE_ERROR;
}
if (0 == memcmp((*it3)->data(), it->pubkey().data(), pubkey_size))
{
it3 = required_keys.erase(it3);
break;
}
}
}
if (forbidden_key_found) return TRANSACTION_VALID_FORBIDDEN_SIGN;
if (!required_keys.size()) return TRANSACTION_VALID_OK;
// TODO: check that given pubkeys are registered for same group
return TRANSACTION_VALID_MISSING_SIGN;
}
}
}

View File

@ -17,18 +17,40 @@
namespace model {
namespace gradido {
enum TransactionValidation {
TRANSACTION_VALID_OK,
TRANSACTION_VALID_MISSING_SIGN,
TRANSACTION_VALID_FORBIDDEN_SIGN,
TRANSACTION_VALID_MISSING_PARAM,
TRANSACTION_VALID_CODE_ERROR,
TRANSACTION_VALID_INVALID_TARGET_DATE,
TRANSACTION_VALID_CREATION_OUT_OF_BORDER,
TRANSACTION_VALID_INVALID_AMOUNT,
TRANSCATION_VALID_INVALID_PUBKEY,
TRANSACTION_VALID_INVALID_GROUP_ALIAS
};
class TransactionBase : public NotificationList, public UniLib::lib::MultithreadContainer
{
public:
TransactionBase(const std::string& memo);
virtual ~TransactionBase();
//! \return 0 if ok, < 0 if error, > 0 if not implemented
virtual int prepare() = 0;
virtual TransactionValidation validate() = 0;
static std::string amountToString(google::protobuf::int64 amount);
inline const std::string& getMemo() const { return mMemo; }
//! \return true if all required signatures are found in signature pairs
TransactionValidation checkRequiredSignatures(const proto::gradido::SignatureMap* sig_map);
protected:
std::string mMemo;
Poco::UInt32 mMinSignatureCount;
std::vector<MemoryBin*> mRequiredSignPublicKeys;
std::vector<MemoryBin*> mForbiddenSignPublicKeys;
};
}
}

View File

@ -91,6 +91,8 @@ namespace model {
return "<uninitalized>";
}
TransactionCreation* TransactionBody::getCreationTransaction()
{
return dynamic_cast<TransactionCreation*>(mTransactionSpecific);

View File

@ -2,6 +2,7 @@
#define GRADIDO_LOGIN_SERVER_MODEL_GRADIDO_TRANSACTION_BASE_H
#include "../../controller/User.h"
#include "../../controller/Group.h"
#include "GroupMemberUpdate.h"
#include "TransactionCreation.h"
#include "TransactionTransfer.h"
@ -20,6 +21,7 @@ namespace model {
TRANSACTION_GROUP_MEMBER_UPDATE
};
class TransactionBody : public Poco::RefCountedObject, UniLib::lib::MultithreadContainer
{

View File

@ -26,8 +26,8 @@ namespace model {
auto receiver_amount = mProtoCreation.receiver();
auto receiverPublic = receiver_amount.pubkey();
if (receiverPublic.size() != 32) {
addError(new Error(functionName, "receiver public invalid (size not 32)"));
if (receiverPublic.size() != KeyPairEd25519::getPublicKeySize()) {
addError(new ParamError(functionName, "receiver public invalid: ", receiverPublic.size()));
return -2;
}
mReceiverUser = controller::User::create();
@ -49,7 +49,11 @@ namespace model {
}*/
}
//
mMinSignatureCount = 1;
auto mm = MemoryManager::getInstance();
auto pubkey_copy = mm->getFreeMemory(KeyPairEd25519::getPublicKeySize());
memcpy(*pubkey_copy, receiverPublic.data(), KeyPairEd25519::getPublicKeySize());
mForbiddenSignPublicKeys.push_back(pubkey_copy);
return 0;
}
@ -62,6 +66,56 @@ namespace model {
return Poco::DateTimeFormatter::format(pocoStamp, "%d. %b %y");
}
TransactionValidation TransactionCreation::validate()
{
static const char function_name[] = "TransactionCreation::validate";
auto target_date = Poco::DateTime(DataTypeConverter::convertFromProtoTimestampSeconds(mProtoCreation.target_date()));
auto now = Poco::DateTime();
if (target_date.year() == now.year())
{
if (target_date.month() + 3 < now.month()) {
addError(new Error(function_name, "year is the same, target date month is more than 3 month in past"));
return TRANSACTION_VALID_INVALID_TARGET_DATE;
}
if (target_date.month() > now.month()) {
addError(new Error(function_name, "year is the same, target date month is in future"));
return TRANSACTION_VALID_INVALID_TARGET_DATE;
}
}
else if(target_date.year() > now.year())
{
addError(new Error(function_name, "target date year is in future"));
return TRANSACTION_VALID_INVALID_TARGET_DATE;
}
else if(target_date.year() +1 < now.year())
{
addError(new Error(function_name, "target date year is in past"));
return TRANSACTION_VALID_INVALID_TARGET_DATE;
}
else
{
// target_date.year +1 == now.year
if (target_date.month() + 3 < now.month() + 12) {
addError(new Error(function_name, "target date is more than 3 month in past"));
return TRANSACTION_VALID_INVALID_TARGET_DATE;
}
}
if (mProtoCreation.receiver().amount() > 1000 * 10000) {
addError(new Error(function_name, "creation amount to high, max 1000 per month"));
return TRANSACTION_VALID_CREATION_OUT_OF_BORDER;
}
if (mProtoCreation.receiver().pubkey().size() != KeyPairEd25519::getPublicKeySize()) {
addError(new Error(function_name, "receiver pubkey has invalid size"));
return TRANSCATION_VALID_INVALID_PUBKEY;
}
// TODO: check creation amount from last 3 month from node server
return TRANSACTION_VALID_OK;
}
}
}

View File

@ -25,6 +25,9 @@ namespace model {
~TransactionCreation();
int prepare();
//! TODO: check created sum in the last 3 month if 1.000 per month isn't exceed
//! maybe ask node server and hope the answer came fast
TransactionValidation validate();
inline Poco::AutoPtr<controller::User> getUser() { return mReceiverUser; }
inline google::protobuf::int64 getAmount() { return mProtoCreation.receiver().amount(); }

View File

@ -97,7 +97,14 @@ namespace model {
else {
mKontoTable.push_back(KontoTableEntry(sender_user->getModel(), amount, true));
}
mMinSignatureCount = 1;
auto mm = MemoryManager::getInstance();
auto pubkey_copy = mm->getFreeMemory(KeyPairEd25519::getPublicKeySize());
memcpy(*pubkey_copy, sender_pubkey.data(), KeyPairEd25519::getPublicKeySize());
mRequiredSignPublicKeys.push_back(pubkey_copy);
}
// TODO: add version for group transfer
@ -118,6 +125,34 @@ namespace model {
return 0;
}
TransactionValidation TransactionTransfer::validate()
{
static const char function_name[] = "TransactionTransfer::validate";
if (!mProtoTransfer.has_local()) {
addError(new Error(function_name, "only local currently implemented"));
return TRANSACTION_VALID_CODE_ERROR;
}
auto amount = mProtoTransfer.local().sender().amount();
if (0 == amount) {
addError(new Error(function_name, "amount is empty"));
return TRANSACTION_VALID_INVALID_AMOUNT;
}
else if (amount < 0) {
addError(new Error(function_name, "negative amount"));
return TRANSACTION_VALID_INVALID_AMOUNT;
}
if (mProtoTransfer.local().receiver().size() != KeyPairEd25519::getPublicKeySize()) {
addError(new Error(function_name, "invalid size of receiver pubkey"));
return TRANSCATION_VALID_INVALID_PUBKEY;
}
if (mProtoTransfer.local().sender().pubkey().size() != KeyPairEd25519::getPublicKeySize()) {
addError(new Error(function_name, "invalid size of sender pubkey"));
return TRANSCATION_VALID_INVALID_PUBKEY;
}
return TRANSACTION_VALID_OK;
}
const std::string& TransactionTransfer::getKontoNameCell(int index)
{

View File

@ -26,6 +26,7 @@ namespace model {
~TransactionTransfer();
int prepare();
TransactionValidation validate();
inline size_t getKontoTableSize() { lock(); size_t s = mKontoTable.size(); unlock(); return s; }
const std::string& getKontoNameCell(int index);

View File

@ -42,7 +42,6 @@ namespace model {
std::string mUrl;
std::string mDescription;
mutable std::shared_mutex mSharedMutex;
};
}

View File

@ -16,6 +16,9 @@
#include <shared_mutex>
//using namespace Poco::Data::Keywords;
#define SHARED_LOCK std::shared_lock<std::shared_mutex> _lock(mSharedMutex)
#define UNIQUE_LOCK std::unique_lock<std::shared_mutex> _lock(mSharedMutex)
namespace model {
namespace table {
@ -42,6 +45,9 @@ namespace model {
size_t updateIntoDB(std::string fieldNames[3], const T1& fieldValue1, const T2& fieldValue2, const T3& fieldValue3);
template<class T>
size_t loadFromDB(const std::string& fieldName, const T& fieldValue);
//! \brief count columes for "SELECT count(id) from <tableName> where <fieldName> = <fieldValue> group by id";
template<class T>
size_t countColumns(const std::string& fieldName, const T& fieldValue);
template<class T>
bool isExistInDB(const std::string& fieldName, const T& fieldValue);
bool isExistInDB();
@ -79,7 +85,9 @@ namespace model {
int mID;
// for poco auto ptr
int mReferenceCount;
int mReferenceCount;
mutable std::shared_mutex mSharedMutex;
};
@ -103,6 +111,31 @@ namespace model {
return resultCount;
}
template<class T>
size_t ModelBase::countColumns(const std::string& fieldName, const T& fieldValue)
{
auto cm = ConnectionManager::getInstance();
Poco::ScopedLock<Poco::Mutex> _lock(mWorkMutex);
auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER);
Poco::Data::Statement select(session);
size_t count = 0;
select
<< "SELECT count(id) from " << getTableName()
<< " where " << fieldName << " LIKE ? group by id"
,Poco::Data::Keywords::into(count)
,Poco::Data::Keywords::useRef(fieldValue);
size_t resultCount = 0;
try {
resultCount = select.execute();
}
catch (Poco::Exception& ex) {
addError(new ParamError(getTableName(), "mysql error by selecting", ex.displayText().data()));
addError(new ParamError(getTableName(), "field name for select: ", fieldName.data()));
}
return count;
}
template<class T>
bool ModelBase::isExistInDB(const std::string& fieldName, const T& fieldValue)
{

View File

@ -36,7 +36,7 @@ namespace model {
static const char* nodeServerTypeToString(NodeServerType type);
inline void setLastLiveSign(Poco::DateTime lastLiveSign) { std::unique_lock<std::shared_mutex> _lock(mSharedMutex); mLastLiveSign = lastLiveSign; }
inline void setLastLiveSign(Poco::DateTime lastLiveSign) { UNIQUE_LOCK; mLastLiveSign = lastLiveSign; }
inline std::string getUrl() const { return mUrl; }
inline int getPort() const { return mPort; }
@ -46,7 +46,7 @@ namespace model {
inline bool isHederaNode() const { return NodeServerIsHederaNode((NodeServerType)mServerType);}
inline bool hasGroup() const {return NodeServerHasGroup((NodeServerType)mServerType);}
inline int getNodeHederaId() const { return mNodeHederaId; }
inline Poco::DateTime getLastLiveSign() const { std::shared_lock<std::shared_mutex> _lock(mSharedMutex); return mLastLiveSign; }
inline Poco::DateTime getLastLiveSign() const { SHARED_LOCK; return mLastLiveSign; }
protected:
Poco::Data::Statement _loadFromDB(Poco::Data::Session session, const std::string& fieldName);
@ -60,8 +60,6 @@ namespace model {
int mServerType;
int mNodeHederaId;
Poco::DateTime mLastLiveSign;
mutable std::shared_mutex mSharedMutex;
};

View File

@ -66,6 +66,16 @@ namespace model
return select;
}
Poco::Data::Statement PendingTask::_loadAllFromDB(Poco::Data::Session session)
{
Poco::Data::Statement select(session);
select << "SELECT id, user_id, request, created, finished, result_json, task_type_id FROM " << getTableName();
return select;
}
Poco::Data::Statement PendingTask::_loadIdFromDB(Poco::Data::Session session)
{
Poco::Data::Statement select(session);

View File

@ -4,8 +4,6 @@
#include "ModelBase.h"
#include "Poco/Types.h"
#include <shared_mutex>
namespace model {
namespace table {
@ -20,12 +18,13 @@ namespace model {
TASK_TYPE_HEDERA_ACCOUNT_CREATE = 25,
};
typedef Poco::Tuple<int, int, Poco::Data::BLOB, Poco::DateTime, Poco::DateTime, std::string, int> PendingTaskTuple;
class PendingTask : public ModelBase
{
public:
PendingTask();
PendingTask(int userId, std::string serializedProtoRequest, TaskType type);
PendingTask(const PendingTaskTuple& tuple);
@ -36,10 +35,13 @@ namespace model {
const char* getTableName() const { return "pending_tasks"; }
std::string toString();
inline int getUserId() const { SHARED_LOCK; return mUserId; }
TaskType getTaskType() const { SHARED_LOCK; return (TaskType)mTaskTypeId; }
static const char* typeToString(TaskType type);
protected:
Poco::Data::Statement _loadFromDB(Poco::Data::Session session, const std::string& fieldName);
Poco::Data::Statement _loadAllFromDB(Poco::Data::Session session);
Poco::Data::Statement _loadIdFromDB(Poco::Data::Session session);
Poco::Data::Statement _insertIntoDB(Poco::Data::Session session);
@ -50,8 +52,6 @@ namespace model {
std::string mResultJsonString;
int mTaskTypeId;
std::shared_mutex mSharedMutex;
};
}

View File

@ -9,7 +9,6 @@
//#include "Poco/Nullable.h"
//#include "Poco/Data/LOB.h"
#include <shared_mutex>
#include "UserRole.h"
@ -34,8 +33,6 @@ namespace model {
class User : public ModelBase
{
public:
#define SHARED_LOCK std::shared_lock<std::shared_mutex> _lock(mSharedMutex)
#define UNIQUE_LOCK std::unique_lock<std::shared_mutex> _lock(mSharedMutex)
User();
User(UserTuple tuple);
User(const std::string& email, const std::string& first_name, const std::string& last_name, int group_id, Poco::UInt64 passwordHashed = 0, std::string languageKey = "de");
@ -123,7 +120,7 @@ namespace model {
// from neighbor tables
Poco::Nullable<int> mRole;
mutable std::shared_mutex mSharedMutex;
};
}
}

View File

@ -0,0 +1,27 @@
#include "GradidoGroupAddMemberTask.h"
#include "../model/gradido/GroupMemberUpdate.h"
GradidoGroupAddMemberTask::GradidoGroupAddMemberTask(model::table::PendingTask* dbModel, Poco::AutoPtr<model::gradido::TransactionBody> transactionBody)
: GradidoTask(dbModel, transactionBody)
{
}
Poco::AutoPtr<controller::PendingTask> GradidoGroupAddMemberTask::create(Poco::AutoPtr<controller::User> user, Poco::AutoPtr<controller::Group> group, const std::string& memo)
{
if (user.isNull() || !user->getModel() || group.isNull() || !group->getModel()) {
return nullptr;
}
static const char* function_name = "GradidoGroupAddMemberTask::create";
auto group_model = group->getModel();
auto transaction = model::gradido::TransactionBody::create(memo, user, proto::gradido::GroupMemberUpdate_MemberUpdateType_ADD_USER, group_model->getAlias());
auto user_model = user->getModel();
auto dbModel = new model::table::PendingTask(user_model->getID(), transaction->getBodyBytes(), model::table::TASK_TYPE_GROUP_ADD_MEMBER);
if (!dbModel->insertIntoDB(true)) {
dbModel->addError(new Error(function_name, "error inserting pending task into db, abort"));
dbModel->sendErrorsAsEmail();
return nullptr;
}
return new GradidoGroupAddMemberTask(dbModel, transaction);
}

View File

@ -0,0 +1,19 @@
#ifndef GRADIDO_LOGIN_SERVER_TASKS_GRADIDO_GROUP_ADD_MEMBER_TASK_H
#define GRADIDO_LOGIN_SERVER_TASKS_GRADIDO_GROUP_ADD_MEMBER_TASK_H
#include "GradidoTask.h"
#include "../controller/User.h"
#include "../controller/Group.h"
class GradidoGroupAddMemberTask : public GradidoTask
{
public:
GradidoGroupAddMemberTask(model::table::PendingTask* dbModel, Poco::AutoPtr<model::gradido::TransactionBody> transactionBody);
static Poco::AutoPtr<controller::PendingTask> create(Poco::AutoPtr<controller::User> user, Poco::AutoPtr<controller::Group> group, const std::string& memo);
protected:
};
#endif //GRADIDO_LOGIN_SERVER_TASKS_GRADIDO_GROUP_ADD_MEMBER_TASK_H

View File

@ -0,0 +1,7 @@
#include "GradidoTask.h"
GradidoTask::GradidoTask(model::table::PendingTask* dbModel, Poco::AutoPtr<model::gradido::TransactionBody> transactionBody)
: controller::PendingTask(dbModel), mGradidoTransactionBody(transactionBody)
{
}

View File

@ -0,0 +1,18 @@
#ifndef GRADIDO_LOGIN_SERVER_TASKS_GRADIDO_TASK
#define GRADIDO_LOGIN_SERVER_TASKS_GRADIDO_TASK
#include "../controller/PendingTask.h"
#include "../model/gradido/TransactionBody.h"
class GradidoTask : public controller::PendingTask, public NotificationList
{
public:
GradidoTask(model::table::PendingTask* dbModel, Poco::AutoPtr<model::gradido::TransactionBody> transactionBody);
bool isTimeoutTask() { return false; }
protected:
Poco::AutoPtr<model::gradido::TransactionBody> mGradidoTransactionBody;
};
#endif //GRADIDO_LOGIN_SERVER_TASKS_GRADIDO_TASK

View File

@ -0,0 +1,12 @@
#ifndef GRADIDO_LOGIN_SERVER_TASKS_PENDING_TASK
#define GRADIDO_LOGIN_SERVER_TASKS_PENDING_TASK
/*
* @author: Dario Rekowski
*
* @date: 13.10.2020
*
* @brief:
*/
#endif //GRADIDO_LOGIN_SERVER_TASKS_PENDING_TASK

View File

@ -24,6 +24,15 @@ ProcessingTransaction::ProcessingTransaction(const std::string& proto_message_ba
}
}
ProcessingTransaction::ProcessingTransaction(Poco::AutoPtr<model::gradido::TransactionBody> transactionBody, DHASH userEmailHash, Languages lang, Poco::DateTime transactionCreated/* = Poco::DateTime()*/)
: mTransactionBody(transactionBody), mUserEmailHash(userEmailHash),
mLang(lang), mTransactionCreated(transactionCreated)
{
auto observer = SingletonTaskObserver::getInstance();
if (userEmailHash != 0) {
observer->addTask(userEmailHash, TASK_OBSERVER_PREPARE_TRANSACTION);
}
}
ProcessingTransaction::~ProcessingTransaction()
{
lock();
@ -75,22 +84,23 @@ int ProcessingTransaction::run()
{
lock();
auto mm = MemoryManager::getInstance();
auto protoMessageBin = DataTypeConverter::base64ToBin(mProtoMessageBase64);
auto langM = LanguageManager::getInstance();
auto catalog = langM->getFreeCatalog(mLang);
if (!protoMessageBin)
{
addError(new Error("ProcessingTransaction", "error decoding base64"));
reportErrorToCommunityServer(catalog->gettext("decoding error"), catalog->gettext("Error decoding base64 string"), "-1");
unlock();
return -1;
if (mProtoMessageBase64 != "") {
auto protoMessageBin = DataTypeConverter::base64ToBin(mProtoMessageBase64);
if (!protoMessageBin)
{
addError(new Error("ProcessingTransaction", "error decoding base64"));
reportErrorToCommunityServer(catalog->gettext("decoding error"), catalog->gettext("Error decoding base64 string"), "-1");
unlock();
return -1;
}
mTransactionBody = model::gradido::TransactionBody::create(protoMessageBin);
mm->releaseMemory(protoMessageBin);
}
mTransactionBody = model::gradido::TransactionBody::create(protoMessageBin);
mm->releaseMemory(protoMessageBin);
if (mTransactionBody.isNull()) {
addError(new Error("ProcessingTransaction", "error creating Transaction from binary Message"));
reportErrorToCommunityServer(catalog->gettext("decoding error"), catalog->gettext("Error by parsing to protobuf message"), "-1");

View File

@ -33,6 +33,7 @@ class ProcessingTransaction : public UniLib::controller::CPUTask, public Notific
public:
//! \param lang for error messages in user language
ProcessingTransaction(const std::string& proto_message_base64, DHASH userEmailHash, Languages lang, Poco::DateTime transactionCreated = Poco::DateTime());
ProcessingTransaction(Poco::AutoPtr<model::gradido::TransactionBody> transactionBody, DHASH userEmailHash, Languages lang, Poco::DateTime transactionCreated = Poco::DateTime());
//ProcessingTransaction(const model::gradido::TransactionBody)
virtual ~ProcessingTransaction();