diff --git a/skeema/gradido_login/pending_tasks.sql b/skeema/gradido_login/pending_tasks.sql index fc98be56f..e3ac7016c 100644 --- a/skeema/gradido_login/pending_tasks.sql +++ b/skeema/gradido_login/pending_tasks.sql @@ -1,6 +1,7 @@ CREATE TABLE `pending_tasks` ( `id` int UNSIGNED NOT NULL AUTO_INCREMENT, - `user_id` int UNSIGNED DEFAULT '0', + `user_id` int UNSIGNED DEFAULT 0, + `hedera_id` int UNSIGNED DEFAULT 0, `request` varbinary(2048) NOT NULL, `created` datetime NOT NULL, `finished` datetime DEFAULT '2000-01-01 000000', diff --git a/skeema/gradido_login/users.sql b/skeema/gradido_login/users.sql index 8fb359f12..4a3c9cc47 100644 --- a/skeema/gradido_login/users.sql +++ b/skeema/gradido_login/users.sql @@ -3,6 +3,7 @@ CREATE TABLE `users` ( `email` varchar(191) NOT NULL, `first_name` varchar(150) NOT NULL, `last_name` varchar(255) DEFAULT '', + `username` varchar(255) DEFAULT '', `password` bigint unsigned NOT NULL, `pubkey` binary(32) DEFAULT NULL, `privkey` binary(80) DEFAULT NULL, diff --git a/src/cpp/JSONInterface/JsonCreateTransaction.cpp b/src/cpp/JSONInterface/JsonCreateTransaction.cpp new file mode 100644 index 000000000..85b96fb26 --- /dev/null +++ b/src/cpp/JSONInterface/JsonCreateTransaction.cpp @@ -0,0 +1,185 @@ +#include "JsonCreateTransaction.h" + +#include "../controller/User.h" + +#include "../lib/DataTypeConverter.h" + +#include "../model/gradido/Transaction.h" + +Poco::JSON::Object* JsonCreateTransaction::handle(Poco::Dynamic::Var params) +{ + auto sm = SessionManager::getInstance(); + + int session_id = 0; + std::string transaction_type; + + // if is json object + if (params.type() == typeid(Poco::JSON::Object::Ptr)) { + Poco::JSON::Object::Ptr paramJsonObject = params.extract(); + /// Throws a RangeException if the value does not fit + /// into the result variable. + /// Throws a NotImplementedException if conversion is + /// not available for the given type. + /// Throws InvalidAccessException if Var is empty. + try { + paramJsonObject->get("session_id").convert(session_id); + paramJsonObject->get("transaction_type").convert(transaction_type); + paramJsonObject->get("memo").convert(mMemo); + } + catch (Poco::Exception& ex) { + return stateError("json exception", ex.displayText()); + } + } + else { + return stateError("parameter format unknown"); + } + + if (!session_id) { + return stateError("session_id invalid"); + } + + mSession = sm->getSession(session_id); + if (!mSession) { + return customStateError("not found", "session not found"); + } + auto user = mSession->getNewUser(); + if (user.isNull()) { + return customStateError("code error", "user is zero"); + } + getTargetGroup(params); + if (transaction_type == "transfer") { + return transfer(params); + } + else if (transaction_type == "creation") { + return creation(params); + } + else if (transaction_type == "groupMemberUpdate") { + return groupMemberUpdate(params); + } + + return stateError("transaction_type unknown"); + +} + +Poco::JSON::Object* JsonCreateTransaction::transfer(Poco::Dynamic::Var params) +{ + auto target_pubkey = getTargetPubkey(params); + Poco::UInt32 amount = 0; + if (params.type() == typeid(Poco::JSON::Object::Ptr)) { + Poco::JSON::Object::Ptr paramJsonObject = params.extract(); + try { + paramJsonObject->get("amount").convert(amount); + } + catch (Poco::Exception& ex) { + return stateError("json exception", ex.displayText()); + } + } + else { + return stateError("parameter format unknown"); + } + + +} +Poco::JSON::Object* JsonCreateTransaction::creation(Poco::Dynamic::Var params) +{ + auto target_pubkey = getTargetPubkey(params); + Poco::UInt32 amount = 0; + Poco::DateTime target_date; + if (params.type() == typeid(Poco::JSON::Object::Ptr)) { + Poco::JSON::Object::Ptr paramJsonObject = params.extract(); + try { + paramJsonObject->get("amount").convert(amount); + paramJsonObject->get("target_date").convert(target_date); + } + catch (Poco::Exception& ex) { + return stateError("json exception", ex.displayText()); + } + } + else { + return stateError("parameter format unknown"); + } + +} +Poco::JSON::Object* JsonCreateTransaction::groupMemberUpdate(Poco::Dynamic::Var params) +{ + if (mTargetGroup.isNull()) { + return stateError("target_group not found"); + } + model::gradido::Transaction::create(mSession->getNewUser(), mTargetGroup); + return stateSuccess(); + +} +MemoryBin* JsonCreateTransaction::getTargetPubkey(Poco::Dynamic::Var params) +{ + std::string email; + std::string target_username; + std::string target_pubkey_hex; + + Poco::JSON::Object::Ptr paramJsonObject = params.extract(); + auto fields = paramJsonObject->getNames(); + try { + for (auto it = fields.begin(); it != fields.end(); it++) { + if (*it == "target_email") { + paramJsonObject->get("target_email").convert(email); + break; + } + if (*it == "target_username") { + paramJsonObject->get("target_username").convert(target_username); + break; + } + if (*it == "target_pubkey") { + paramJsonObject->get("target_pubkey").convert(target_pubkey_hex); + break; + } + } + } + catch (Poco::Exception& ex) { + return nullptr; + } + auto user = controller::User::create(); + int result_count = 0; + + MemoryBin* result = nullptr; + if (email != "") { + result_count = user->load(email); + } + else if (target_username != "") { + int group_id = 0; + if (!mTargetGroup.isNull()) { + group_id = mTargetGroup->getModel()->getID(); + } else { + mSession->getNewUser()->getModel()->getGroupId(); + } + result_count = user->getModel()->loadFromDB({ "username", "group_id" }, target_username, group_id, model::table::MYSQL_CONDITION_AND); + } + else if (target_pubkey_hex != "") { + result = DataTypeConverter::hexToBin(target_pubkey_hex); + } + if (1 == result_count) { + result = user->getModel()->getPublicKeyCopy(); + } + return result; +} + +bool JsonCreateTransaction::getTargetGroup(Poco::Dynamic::Var params) +{ + std::string target_group_alias; + Poco::JSON::Object::Ptr paramJsonObject = params.extract(); + try + { + auto target_group = paramJsonObject->get("target_group"); + if (!target_group.isEmpty()) { + target_group.convert(target_group_alias); + auto groups = controller::Group::load(target_group_alias); + if (groups.size() == 1) { + mTargetGroup = groups[0]; + return true; + } + } + + } + catch (Poco::Exception& ex) { + return false; + } + return false; +} \ No newline at end of file diff --git a/src/cpp/JSONInterface/JsonCreateTransaction.h b/src/cpp/JSONInterface/JsonCreateTransaction.h new file mode 100644 index 000000000..828ee8aa0 --- /dev/null +++ b/src/cpp/JSONInterface/JsonCreateTransaction.h @@ -0,0 +1,26 @@ +#ifndef __JSON_INTERFACE_JSON_CREATE_TRANSACTION_ +#define __JSON_INTERFACE_JSON_CREATE_TRANSACTION_ + +#include "JsonRequestHandler.h" +#include "../SingletonManager/SessionManager.h" +#include "../controller/Group.h" + +class JsonCreateTransaction : public JsonRequestHandler +{ +public: + JsonCreateTransaction() : mSession(nullptr) {} + Poco::JSON::Object* handle(Poco::Dynamic::Var params); + +protected: + Poco::JSON::Object* transfer(Poco::Dynamic::Var params); + Poco::JSON::Object* creation(Poco::Dynamic::Var params); + Poco::JSON::Object* groupMemberUpdate(Poco::Dynamic::Var params); + MemoryBin* getTargetPubkey(Poco::Dynamic::Var params); + bool getTargetGroup(Poco::Dynamic::Var params); + + Session* mSession; + std::string mMemo; + Poco::AutoPtr mTargetGroup; +}; + +#endif // __JSON_INTERFACE_JSON_CREATE_TRANSACTION_ \ No newline at end of file diff --git a/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp b/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp index 33ce5c38e..d0da6b67f 100644 --- a/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp +++ b/src/cpp/JSONInterface/JsonRequestHandlerFactory.cpp @@ -4,13 +4,14 @@ #include "../SingletonManager/SessionManager.h" +#include "JsonAdminEmailVerificationResend.h" +#include "JsonCreateTransaction.h" #include "JsonCreateUser.h" #include "JsonGetLogin.h" #include "JsonUnknown.h" #include "JsonTransaction.h" #include "JsonGetRunningUserTasks.h" #include "JsonGetUsers.h" -#include "JsonAdminEmailVerificationResend.h" #include "JsonGetUserInfos.h" #include "JsonUpdateUserInfos.h" #include "JsonUnsecureLogin.h" @@ -47,6 +48,9 @@ Poco::Net::HTTPRequestHandler* JsonRequestHandlerFactory::createRequestHandler(c else if (url_first_part == "/checkTransaction") { return new JsonTransaction; } + else if (url_first_part == "/createTransaction") { + return new JsonCreateTransaction; + } else if (url_first_part == "/getRunningUserTasks") { return new JsonGetRunningUserTasks; } diff --git a/src/cpp/controller/Group.cpp b/src/cpp/controller/Group.cpp index f835e6986..4692c0f10 100644 --- a/src/cpp/controller/Group.cpp +++ b/src/cpp/controller/Group.cpp @@ -32,6 +32,18 @@ namespace controller { } return resultVector; } + + Poco::AutoPtr Group::load(int id) + { + auto db = new model::table::Group(); + if (1 == db->loadFromDB("id", id)) { + return new Group(db); + } + else { + return nullptr; + } + } + std::vector> Group::listAll() { diff --git a/src/cpp/controller/Group.h b/src/cpp/controller/Group.h index e1ff29b64..8ebf883c2 100644 --- a/src/cpp/controller/Group.h +++ b/src/cpp/controller/Group.h @@ -17,6 +17,7 @@ namespace controller { static Poco::AutoPtr create(const std::string& alias, const std::string& name, const std::string& url, const std::string& description); static std::vector> load(const std::string& alias); + static Poco::AutoPtr load(int id); static std::vector> listAll(); inline bool deleteFromDB() { return mDBModel->deleteFromDB(); } diff --git a/src/cpp/lib/DataTypeConverter.cpp b/src/cpp/lib/DataTypeConverter.cpp index a5437da7e..70ceb8055 100644 --- a/src/cpp/lib/DataTypeConverter.cpp +++ b/src/cpp/lib/DataTypeConverter.cpp @@ -277,6 +277,22 @@ namespace DataTypeConverter return microseconds; } + void convertToProtoTimestamp(const Poco::Timestamp pocoTimestamp, proto::Timestamp* protoTimestamp) + { + auto microsecondsTotal = pocoTimestamp.epochMicroseconds(); + auto secondsTotal = pocoTimestamp.epochTime(); + protoTimestamp->set_seconds(secondsTotal); + protoTimestamp->set_nanos((microsecondsTotal - secondsTotal * pocoTimestamp.resolution()) * 1000); + } + + void convertToProtoTimestamp(const Poco::Timestamp pocoTimestamp, proto::gradido::Timestamp* protoTimestamp) + { + auto microsecondsTotal = pocoTimestamp.epochMicroseconds(); + auto secondsTotal = pocoTimestamp.epochTime(); + protoTimestamp->set_seconds(secondsTotal); + protoTimestamp->set_nanos((microsecondsTotal - secondsTotal * pocoTimestamp.resolution()) * 1000); + } + Poco::Timestamp convertFromProtoTimestampSeconds(const proto::gradido::TimestampSeconds& timestampSeconds) { google::protobuf::int64 microseconds = timestampSeconds.seconds() * (google::protobuf::int64)10e5; diff --git a/src/cpp/lib/DataTypeConverter.h b/src/cpp/lib/DataTypeConverter.h index aceb6ac1e..d6783c977 100644 --- a/src/cpp/lib/DataTypeConverter.h +++ b/src/cpp/lib/DataTypeConverter.h @@ -51,6 +51,8 @@ namespace DataTypeConverter { std::string convertTimespanToLocalizedString(Poco::Timespan duration, LanguageCatalog* lang); Poco::Timestamp convertFromProtoTimestamp(const proto::Timestamp& timestamp); + void convertToProtoTimestamp(const Poco::Timestamp pocoTimestamp, proto::Timestamp* protoTimestamp); + void convertToProtoTimestamp(const Poco::Timestamp pocoTimestamp, proto::gradido::Timestamp* protoTimestamp); Poco::Timestamp convertFromProtoTimestampSeconds(const proto::gradido::TimestampSeconds& timestampSeconds); Poco::Timespan convertFromProtoDuration(const proto::Duration& duration); }; diff --git a/src/cpp/model/gradido/GroupMemberUpdate.cpp b/src/cpp/model/gradido/GroupMemberUpdate.cpp index 47bc9c28e..d8e898603 100644 --- a/src/cpp/model/gradido/GroupMemberUpdate.cpp +++ b/src/cpp/model/gradido/GroupMemberUpdate.cpp @@ -101,5 +101,32 @@ namespace model { auto user_pubkey = mProtoMemberUpdate.user_pubkey(); return DataTypeConverter::binToHex((const unsigned char*)user_pubkey.data(), user_pubkey.size()); } + + void GroupMemberUpdate::transactionAccepted(Poco::AutoPtr user) + { + static const char* function_name = "GroupMemberUpdate::transactionAccepted"; + auto sm = SessionManager::getInstance(); + + auto target_group = mProtoMemberUpdate.target_group(); + + if (sm->isValid(target_group, VALIDATE_GROUP_ALIAS)) { + auto groups = controller::Group::load(mProtoMemberUpdate.target_group()); + if (groups.size() != 1) { + addError(new ParamError(function_name, "target group not known or not unambiguous: ", target_group)); + sendErrorsAsEmail(); + } + else { + auto user_model = user->getModel(); + auto group_model = groups[0]->getModel(); + // write new group_id in user table + user_model->setGroupId(group_model->getID()); + user_model->updateIntoDB("group_id", group_model->getID()); + } + } + else { + addError(new ParamError(function_name, "invalid group alias, after transaction was successfully sended: ", target_group)); + sendErrorsAsEmail(); + } + } } } \ No newline at end of file diff --git a/src/cpp/model/gradido/GroupMemberUpdate.h b/src/cpp/model/gradido/GroupMemberUpdate.h index 076a14d71..7bd146cfd 100644 --- a/src/cpp/model/gradido/GroupMemberUpdate.h +++ b/src/cpp/model/gradido/GroupMemberUpdate.h @@ -18,6 +18,8 @@ namespace model { std::string getTargetGroupAlias() { return mProtoMemberUpdate.target_group(); } std::string getPublicKeyHex(); + void transactionAccepted(Poco::AutoPtr); + protected: const proto::gradido::GroupMemberUpdate& mProtoMemberUpdate; }; diff --git a/src/cpp/model/gradido/Transaction.cpp b/src/cpp/model/gradido/Transaction.cpp index 2a8f34b96..1c8d9ba5e 100644 --- a/src/cpp/model/gradido/Transaction.cpp +++ b/src/cpp/model/gradido/Transaction.cpp @@ -44,19 +44,134 @@ namespace model { Poco::AutoPtr Transaction::create(Poco::AutoPtr user, Poco::AutoPtr group) { + auto em = ErrorManager::getInstance(); + static const char* function_name = "Transaction::create group member update"; + if (user.isNull() || !user->getModel() || group.isNull() || !group->getModel()) { return nullptr; } auto group_model = group->getModel(); + auto network_type = table::HEDERA_TESTNET; + auto topic_id = controller::HederaId::find(group_model->getID(), network_type); + + if (topic_id.isNull()) { + em->addError(new ParamError(function_name, "could'n find topic for group: ", group_model->getID())); + em->addError(new ParamError(function_name, "network type: ", network_type)); + em->sendErrorsAsEmail(); + return nullptr; + } + auto body = TransactionBody::create("", user, proto::gradido::GroupMemberUpdate_MemberUpdateType_ADD_USER, group_model->getAlias()); Poco::AutoPtr result = new Transaction(body); auto model = result->getModel(); + model->setHederaId(topic_id->getModel()->getID()); result->insertPendingTaskIntoDB(user, model::table::TASK_TYPE_GROUP_ADD_MEMBER); PendingTasksManager::getInstance()->addTask(result); return result; } + Poco::AutoPtr Transaction::create(Poco::AutoPtr receiver, Poco::UInt32 amount, Poco::DateTime targetDate, const std::string& memo) + { + auto em = ErrorManager::getInstance(); + static const char* function_name = "Transaction::create creation"; + + if (receiver.isNull() || !receiver->getModel()) { + return nullptr; + } + auto network_type = table::HEDERA_TESTNET; + auto receiver_model = receiver->getModel(); + auto topic_id = controller::HederaId::find(receiver_model->getGroupId(), network_type); + + if (topic_id.isNull()) { + em->addError(new ParamError(function_name, "could'n find topic for group: ", receiver_model->getGroupId())); + em->addError(new ParamError(function_name, "network type: ", network_type)); + em->sendErrorsAsEmail(); + return nullptr; + } + auto body = TransactionBody::create(memo, receiver, amount, targetDate); + Poco::AutoPtr result = new Transaction(body); + auto model = result->getModel(); + model->setHederaId(topic_id->getModel()->getID()); + result->insertPendingTaskIntoDB(receiver, model::table::TASK_TYPE_CREATION); + PendingTasksManager::getInstance()->addTask(result); + return result; + } + + std::vector> Transaction::create(Poco::AutoPtr sender, MemoryBin* receiverPubkey, Poco::AutoPtr receiverGroup, Poco::UInt32 amount, const std::string& memo) + { + std::vector> results; + auto em = ErrorManager::getInstance(); + static const char* function_name = "Transaction::create transfer"; + + if (sender.isNull() || !sender->getModel() || receiverGroup.isNull() || !receiverGroup->getModel() || !receiverPubkey || !amount) { + return results; + } + + //std::vector> bodys; + auto sender_model = sender->getModel(); + auto group_model = receiverGroup->getModel(); + auto network_type = table::HEDERA_TESTNET; + // LOCAL Transfer + if (sender_model->getGroupId() == group_model->getID()) + { + auto body = TransactionBody::create(memo, sender, receiverPubkey, amount); + Poco::AutoPtr transaction = new Transaction(body); + auto topic_id = controller::HederaId::find(sender_model->getGroupId(), network_type); + if (topic_id.isNull()) { + em->addError(new ParamError(function_name, "could'n find topic for group: ", sender_model->getGroupId())); + em->addError(new ParamError(function_name, "network type: ", network_type)); + em->sendErrorsAsEmail(); + return results; + } + transaction->getModel()->setHederaId(topic_id->getModel()->getID()); + results.push_back(transaction); + } + else + { + auto sender_group = controller::Group::load(sender_model->getGroupId()); + Poco::AutoPtr transaction_group; + Poco::AutoPtr topic_group; + // default constructor set it to now + Poco::Timestamp pairedTransactionId; + for (int i = 0; i < 2; i++) { + if (0 == i) { + transaction_group = receiverGroup; + topic_group = sender_group; + } + else { + transaction_group = sender_group; + topic_group = receiverGroup; + } + auto topic_id = controller::HederaId::find(topic_group->getModel()->getID(), network_type); + if (topic_id.isNull()) { + em->addError(new ParamError(function_name, "could'n find topic for group: ", sender_model->getGroupId())); + em->addError(new ParamError(function_name, "network type: ", network_type)); + em->sendErrorsAsEmail(); + return results; + } + if (transaction_group.isNull()) { + em->addError(new ParamError(function_name, "transaction group is zero, i:", i)); + em->sendErrorsAsEmail(); + return results; + } + + auto body = TransactionBody::create(memo, sender, receiverPubkey, amount, pairedTransactionId, transaction_group); + Poco::AutoPtr transaction = new Transaction(body); + transaction->getModel()->setHederaId(topic_id->getModel()->getID()); + results.push_back(transaction); + } + } + for (auto it = results.begin(); it != results.end(); it++) { + (*it)->insertPendingTaskIntoDB(sender, model::table::TASK_TYPE_TRANSFER); + PendingTasksManager::getInstance()->addTask(*it); + } + return results; + + } + + + Poco::AutoPtr Transaction::load(model::table::PendingTask* dbModel) { proto::gradido::GradidoTransaction transaction_temp; @@ -333,8 +448,9 @@ namespace model { // send transaction via hedera auto network_type = table::HEDERA_TESTNET; // TODO: get correct topic id for user group - int user_group_id = 1; - auto topic_id = controller::HederaId::find(user_group_id, network_type); + //int user_group_id = 1; + //auto topic_id = controller::HederaId::find(user_group_id, network_type); + auto topic_id = controller::HederaId::load(getModel()->getHederaId()); auto hedera_operator_account = controller::HederaAccount::pick(network_type, false); if (!topic_id.isNull() && !hedera_operator_account.isNull()) @@ -357,9 +473,9 @@ namespace model { hedera_transaction.sign(crypto_key->getKeyPair(), std::move(hedera_transaction_body)); HederaRequest hedera_request; - HederaTask hedera_task(this); + Poco::AutoPtr hedera_task(new HederaTask(this)); - if (HEDERA_REQUEST_RETURN_OK != hedera_request.request(&hedera_transaction, &hedera_task)) + if (HEDERA_REQUEST_RETURN_OK != hedera_request.request(&hedera_transaction, hedera_task)) { addError(new Error(function_name, "error send transaction to hedera")); getErrors(&hedera_request); @@ -367,7 +483,7 @@ namespace model { return -2; } else { - auto hedera_transaction_response = hedera_task.getTransactionResponse(); + auto hedera_transaction_response = hedera_task->getTransactionResponse(); auto hedera_precheck_code_string = hedera_transaction_response->getPrecheckCodeString(); auto precheck_code = hedera_transaction_response->getPrecheckCode(); auto cost = hedera_transaction_response->getCost(); @@ -377,6 +493,12 @@ namespace model { int zahl = 0; return -5; } + else if (precheck_code == proto::OK) { + // simply assume if transaction was sended to hedera without error, it was also accepted from gradido node + // TODO: later check, but now I haven't any way to communicate with the gradido node + mTransactionBody->getTransactionBase()->transactionAccepted(getUser()); + return 1; + } } //model::hedera::TransactionBody hedera_transaction_body() @@ -391,7 +513,7 @@ namespace model { else { addError(new Error(function_name, "hedera topic id or operator account not found!")); - addError(new ParamError(function_name, "user group id: ", user_group_id)); + addError(new ParamError(function_name, "topic id: ", topic_id->getModel()->toString())); addError(new ParamError(function_name, "network type: ", network_type)); sendErrorsAsEmail(); return -4; @@ -412,10 +534,14 @@ namespace model { int SendTransactionTask::run() { auto result = mTransaction->runSendTransaction(); - + // delete because of error if (-1 == result) { mTransaction->deleteFromDB(); } + // delete because succeed, maybe change later + if (1 == result) { + mTransaction->deleteFromDB(); + } return 0; } diff --git a/src/cpp/model/gradido/Transaction.h b/src/cpp/model/gradido/Transaction.h index 1345a7edf..0e2c0f91a 100644 --- a/src/cpp/model/gradido/Transaction.h +++ b/src/cpp/model/gradido/Transaction.h @@ -26,7 +26,13 @@ namespace model { ~Transaction(); // create group add member transaction + // groupMemberUpdate static Poco::AutoPtr create(Poco::AutoPtr user, Poco::AutoPtr group); + //! \brief transfer + //! \return for cross group transaction return two transactions + static std::vector> create(Poco::AutoPtr sender, MemoryBin* receiverPubkey, Poco::AutoPtr receiverGroup, Poco::UInt32 amount, const std::string& memo); + + static Poco::AutoPtr create(Poco::AutoPtr receiver, Poco::UInt32 amount, Poco::DateTime targetDate, const std::string& memo); static Poco::AutoPtr load(model::table::PendingTask* dbModel); bool sign(Poco::AutoPtr user); diff --git a/src/cpp/model/gradido/TransactionBase.h b/src/cpp/model/gradido/TransactionBase.h index 6b2baca8f..eb67d8d55 100644 --- a/src/cpp/model/gradido/TransactionBase.h +++ b/src/cpp/model/gradido/TransactionBase.h @@ -15,6 +15,8 @@ #include "../proto/gradido/BasicTypes.pb.h" #include "../SingletonManager/MemoryManager.h" +#include "../controller/User.h" + namespace model { namespace gradido { @@ -54,6 +56,9 @@ namespace model { inline Poco::UInt32 getMinSignatureCount() { return mMinSignatureCount; } + // called after sending transaction over hedera and after they was accepted from gradido node (at least one) + virtual void transactionAccepted(Poco::AutoPtr user) = 0; + protected: std::string mMemo; Poco::UInt32 mMinSignatureCount; diff --git a/src/cpp/model/gradido/TransactionBody.cpp b/src/cpp/model/gradido/TransactionBody.cpp index e05554252..294f33d34 100644 --- a/src/cpp/model/gradido/TransactionBody.cpp +++ b/src/cpp/model/gradido/TransactionBody.cpp @@ -36,6 +36,81 @@ namespace model { return obj; } + Poco::AutoPtr TransactionBody::create(const std::string& memo, Poco::AutoPtr sender, MemoryBin* receiverPublicKey, Poco::UInt32 amount, Poco::Timestamp pairedTransactionId, Poco::AutoPtr group/* = nullptr*/) + { + if (sender.isNull() || !sender->getModel()) { + return nullptr; + } + auto sender_model = sender->getModel(); + + + Poco::AutoPtr obj = new TransactionBody; + obj->mTransactionBody.set_memo(memo); + auto gradido_transfer = obj->mTransactionBody.mutable_transfer(); + proto::gradido::TransferAmount* transfer_amount = nullptr; + std::string* receiver = nullptr; + + + if (group.isNull()) + { + auto local = gradido_transfer->mutable_local(); + transfer_amount = local->mutable_sender(); + receiver = local->mutable_receiver(); + } + else + { + auto group_model = group->getModel(); + proto::gradido::CrossGroupTransfer* cross_group_transfer = nullptr; + if (group->getModel()->getID() == sender->getModel()->getGroupId()) { + cross_group_transfer = gradido_transfer->mutable_outbound(); + } + else { + cross_group_transfer = gradido_transfer->mutable_inbound(); + } + transfer_amount = cross_group_transfer->mutable_sender(); + receiver = cross_group_transfer->mutable_receiver(); + auto paired_transaction_id = cross_group_transfer->mutable_paired_transaction_id(); + DataTypeConverter::convertToProtoTimestamp(pairedTransactionId, paired_transaction_id); + cross_group_transfer->set_other_group(group_model->getAlias()); + } + transfer_amount->set_amount(amount); + transfer_amount->set_pubkey(sender_model->getPublicKey(), sender_model->getPublicKeySize()); + *receiver = std::string((const char*)*receiverPublicKey, receiverPublicKey->size()); + + obj->mType = TRANSACTION_TRANSFER; + obj->mTransactionSpecific = new TransactionTransfer(memo, obj->mTransactionBody.transfer()); + obj->mTransactionSpecific->prepare(); + + return obj; + } + + Poco::AutoPtr TransactionBody::load(const std::string& memo, Poco::AutoPtr receiver, Poco::UInt32 amount, Poco::DateTime targetDate) + { + if (receiver.isNull() || !receiver->getModel()) { + return nullptr; + } + auto receiver_model = receiver->getModel(); + + Poco::AutoPtr obj = new TransactionBody; + obj->mTransactionBody.set_memo(memo); + + auto creation = obj->mTransactionBody.mutable_creation(); + auto target_date_timestamp_seconds = creation->mutable_target_date(); + target_date_timestamp_seconds->set_seconds(targetDate.timestamp().epochTime()); + + auto transfer_amount = creation->mutable_receiver(); + transfer_amount->set_amount(amount); + std::string* pubkey_str = transfer_amount->mutable_pubkey(); + *pubkey_str = std::string((const char*)receiver_model->getPublicKey(), receiver_model->getPublicKeySize()); + + obj->mType = TRANSACTION_CREATION; + obj->mTransactionSpecific = new TransactionCreation(memo, obj->mTransactionBody.creation()); + obj->mTransactionSpecific->prepare(); + + return obj; + + } + Poco::AutoPtr TransactionBody::load(const std::string& protoMessageBin) { Poco::AutoPtr obj = new TransactionBody; diff --git a/src/cpp/model/gradido/TransactionBody.h b/src/cpp/model/gradido/TransactionBody.h index e58fcd019..6168d2884 100644 --- a/src/cpp/model/gradido/TransactionBody.h +++ b/src/cpp/model/gradido/TransactionBody.h @@ -28,7 +28,16 @@ namespace model { public: ~TransactionBody(); + //! \brief GroupMemberUpdate Transaction static Poco::AutoPtr create(const std::string& memo, Poco::AutoPtr user, proto::gradido::GroupMemberUpdate_MemberUpdateType type, const std::string& targetGroupAlias); + //! \brief GradidoTransfer Transaction + //! \param group if group.isNull() it is a local transfer, else cross group transfer, + //! \param group if group is same as sender group outbound, else inbound + static Poco::AutoPtr create(const std::string& memo, Poco::AutoPtr sender, MemoryBin* receiverPublicKey, Poco::UInt32 amount, Poco::Timestamp pairedTransactionId = Poco::Timestamp(), Poco::AutoPtr group = nullptr); + //! \brief GradidoCreation Transaction + static Poco::AutoPtr create(const std::string& memo, Poco::AutoPtr receiver, Poco::UInt32 amount, Poco::DateTime targetDate); + + static Poco::AutoPtr load(const std::string& protoMessageBin); inline TransactionType getType() { lock(); auto t = mType; unlock(); return t; } diff --git a/src/cpp/model/gradido/TransactionCreation.cpp b/src/cpp/model/gradido/TransactionCreation.cpp index a2db5a93a..e53a5544b 100644 --- a/src/cpp/model/gradido/TransactionCreation.cpp +++ b/src/cpp/model/gradido/TransactionCreation.cpp @@ -117,6 +117,11 @@ namespace model { return TRANSACTION_VALID_OK; } + void TransactionCreation::transactionAccepted(Poco::AutoPtr user) + { + + } + } } diff --git a/src/cpp/model/gradido/TransactionCreation.h b/src/cpp/model/gradido/TransactionCreation.h index a7c64c250..7304f49cd 100644 --- a/src/cpp/model/gradido/TransactionCreation.h +++ b/src/cpp/model/gradido/TransactionCreation.h @@ -36,6 +36,8 @@ namespace model { inline std::string getAmountString() { return amountToString(getAmount()); } std::string getTargetDateString(); + void transactionAccepted(Poco::AutoPtr user); + protected: const proto::gradido::GradidoCreation& mProtoCreation; char mReceiverPublicHex[65]; diff --git a/src/cpp/model/gradido/TransactionTransfer.cpp b/src/cpp/model/gradido/TransactionTransfer.cpp index 125acb1bf..e730262b7 100644 --- a/src/cpp/model/gradido/TransactionTransfer.cpp +++ b/src/cpp/model/gradido/TransactionTransfer.cpp @@ -179,6 +179,12 @@ namespace model { return mKontoTable[index].amountCell; } + void TransactionTransfer::transactionAccepted(Poco::AutoPtr user) + { + + } + + } } diff --git a/src/cpp/model/gradido/TransactionTransfer.h b/src/cpp/model/gradido/TransactionTransfer.h index 3c4e60a55..0d78befd5 100644 --- a/src/cpp/model/gradido/TransactionTransfer.h +++ b/src/cpp/model/gradido/TransactionTransfer.h @@ -32,6 +32,8 @@ namespace model { const std::string& getKontoNameCell(int index); const std::string& getAmountCell(int index); + void transactionAccepted(Poco::AutoPtr user); + protected: const static std::string mInvalidIndexMessage; diff --git a/src/cpp/model/table/PendingTask.cpp b/src/cpp/model/table/PendingTask.cpp index 472e403df..3918ad254 100644 --- a/src/cpp/model/table/PendingTask.cpp +++ b/src/cpp/model/table/PendingTask.cpp @@ -20,8 +20,9 @@ namespace model } PendingTask::PendingTask(const PendingTaskTuple& tuple) - : ModelBase(tuple.get<0>()), mUserId(tuple.get<1>()), mRequest(tuple.get<2>()), mCreated(tuple.get<3>()), mFinished(tuple.get<4>()), - mResultJsonString(tuple.get<5>()), mTaskTypeId(tuple.get<6>()), mChildPendingTaskId(tuple.get<7>()), mParentPendingTaskId(tuple.get<8>()) + : ModelBase(tuple.get<0>()), mUserId(tuple.get<1>()), mHederaId(tuple.get<2>()), + mRequest(tuple.get<3>()), mCreated(tuple.get<4>()), mFinished(tuple.get<5>()), + mResultJsonString(tuple.get<6>()), mTaskTypeId(tuple.get<7>()), mChildPendingTaskId(tuple.get<8>()), mParentPendingTaskId(tuple.get<9>()) { } @@ -113,9 +114,9 @@ namespace model { Poco::Data::Statement select(session); - select << "SELECT id, user_id, request, created, finished, result_json, task_type_id, child_pending_task_id, parent_pending_task_id FROM " << getTableName() + select << "SELECT id, user_id, hedera_id, request, created, finished, result_json, task_type_id, child_pending_task_id, parent_pending_task_id FROM " << getTableName() << " where " << fieldName << " = ?" - , into(mID), into(mUserId), into(mRequest), into(mCreated), into(mFinished), into(mResultJsonString), + , into(mID), into(mUserId), into(mHederaId), into(mRequest), into(mCreated), into(mFinished), into(mResultJsonString), into(mTaskTypeId), into(mChildPendingTaskId), into(mParentPendingTaskId); return select; @@ -125,7 +126,7 @@ namespace model { Poco::Data::Statement select(session); - select << "SELECT id, user_id, request, created, finished, result_json, task_type_id, child_pending_task_id, parent_pending_task_id FROM " << getTableName(); + select << "SELECT id, user_id, hedera_id, request, created, finished, result_json, task_type_id, child_pending_task_id, parent_pending_task_id FROM " << getTableName(); return select; } @@ -149,8 +150,8 @@ namespace model Poco::Data::Statement insert(session); lock(); insert << "INSERT INTO " << getTableName() - << " (user_id, request, created, task_type_id, child_pending_task_id, parent_pending_task_id) VALUES(?,?,?,?,?,?)" - , use(mUserId), use(mRequest), use(mCreated), use(mTaskTypeId), use(mChildPendingTaskId), use(mParentPendingTaskId); + << " (user_id, hedera_id, request, created, task_type_id, child_pending_task_id, parent_pending_task_id) VALUES(?,?,?,?,?,?)" + , use(mUserId), use(mHederaId), use(mRequest), use(mCreated), use(mTaskTypeId), use(mChildPendingTaskId), use(mParentPendingTaskId); unlock(); return insert; } diff --git a/src/cpp/model/table/PendingTask.h b/src/cpp/model/table/PendingTask.h index afafafb33..fd93920a2 100644 --- a/src/cpp/model/table/PendingTask.h +++ b/src/cpp/model/table/PendingTask.h @@ -21,7 +21,7 @@ namespace model { }; - typedef Poco::Tuple PendingTaskTuple; + typedef Poco::Tuple PendingTaskTuple; class PendingTask : public ModelBase { @@ -41,6 +41,7 @@ namespace model { bool updateRequest(); inline int getUserId() const { SHARED_LOCK; return mUserId; } + inline int getHederaId() const { SHARED_LOCK; return mHederaId; } inline const std::vector& getRequest() const { SHARED_LOCK; return mRequest.content(); } inline std::string getRequestCopy() const { SHARED_LOCK; return std::string((const char*)mRequest.content().data(), mRequest.content().size()); } inline Poco::DateTime getCreated() const { SHARED_LOCK; return mCreated; } @@ -49,6 +50,7 @@ namespace model { inline int getParentPendingTaskId() const { SHARED_LOCK; return mParentPendingTaskId; } inline void setUserId(int userId) { UNIQUE_LOCK; mUserId = userId; } + inline void setHederaId(int hederaId) { UNIQUE_LOCK; mHederaId = hederaId; } void setRequest(const std::string& serializedProto); inline void setTaskType(TaskType type) { UNIQUE_LOCK; mTaskTypeId = type; } inline void setChildPendingTaskId(int childPendingTaskId) {UNIQUE_LOCK; mChildPendingTaskId = childPendingTaskId;} @@ -67,6 +69,7 @@ namespace model { Poco::Data::Statement _insertIntoDB(Poco::Data::Session session); int mUserId; + int mHederaId; Poco::Data::BLOB mRequest; Poco::DateTime mCreated; Poco::DateTime mFinished; diff --git a/src/cpp/model/table/User.cpp b/src/cpp/model/table/User.cpp index d45147532..e3797fcf1 100644 --- a/src/cpp/model/table/User.cpp +++ b/src/cpp/model/table/User.cpp @@ -26,8 +26,8 @@ namespace model { //id, first_name, last_name, email, pubkey, created, email_checked User::User(UserTuple tuple) : ModelBase(tuple.get<0>()), - mFirstName(tuple.get<1>()), mLastName(tuple.get<2>()), mEmail(tuple.get<3>()), - mPublicKey(tuple.get<4>()), mCreated(tuple.get<5>()), mEmailChecked(tuple.get<6>()), mDisabled(tuple.get<7>()), mGroupId(tuple.get<8>()), + mFirstName(tuple.get<1>()), mLastName(tuple.get<2>()), mEmail(tuple.get<3>()), mUsername(tuple.get<4>()), + mPublicKey(tuple.get<5>()), mCreated(tuple.get<6>()), mEmailChecked(tuple.get<7>()), mDisabled(tuple.get<8>()), mGroupId(tuple.get<9>()), mPasswordHashed(0), mLanguageKey("de"), mRole(ROLE_NOT_LOADED) { @@ -80,12 +80,12 @@ namespace model { if (mPasswordHashed) { - insert << "INSERT INTO users (email, first_name, last_name, password, email_hash, language, group_id) VALUES(?,?,?,?,?,?,?);", - use(mEmail), use(mFirstName), use(mLastName), bind(mPasswordHashed), use(mEmailHash), use(mLanguageKey), use(mGroupId); + insert << "INSERT INTO users (email, first_name, last_name, username, password, email_hash, language, group_id) VALUES(?,?,?,?,?,?,?,?);", + use(mEmail), use(mFirstName), use(mLastName), use(mUsername), bind(mPasswordHashed), use(mEmailHash), use(mLanguageKey), use(mGroupId); } else { - insert << "INSERT INTO users (email, first_name, last_name, email_hash, language, group_id) VALUES(?,?,?,?,?,?);", - use(mEmail), use(mFirstName), use(mLastName), use(mEmailHash), use(mLanguageKey), use(mGroupId); + insert << "INSERT INTO users (email, first_name, last_name, username, email_hash, language, group_id) VALUES(?,?,?,?,?,?,?);", + use(mEmail), use(mFirstName), use(mLastName), use(mUsername), use(mEmailHash), use(mLanguageKey), use(mGroupId); } return insert; @@ -98,11 +98,11 @@ namespace model { _fieldName = getTableName() + std::string(".id"); } Poco::Data::Statement select(session); - select << "SELECT " << getTableName() << ".id, email, first_name, last_name, password, pubkey, privkey, email_hash, created, email_checked, language, disabled, group_id, user_roles.role_id " + select << "SELECT " << getTableName() << ".id, email, first_name, last_name, username, password, pubkey, privkey, email_hash, created, email_checked, language, disabled, group_id, user_roles.role_id " << " FROM " << getTableName() << " LEFT JOIN user_roles ON " << getTableName() << ".id = user_roles.user_id " << " WHERE " << _fieldName << " = ?" , - into(mID), into(mEmail), into(mFirstName), into(mLastName), into(mPasswordHashed), + into(mID), into(mEmail), into(mFirstName), into(mLastName), into(mUsername), into(mPasswordHashed), into(mPublicKey), into(mPrivateKey), into(mEmailHash), into(mCreated), into(mEmailChecked), into(mLanguageKey), into(mDisabled), into(mGroupId), into(mRole); @@ -114,7 +114,7 @@ namespace model { { Poco::Data::Statement select(session); // typedef Poco::Tuple, int> UserTuple; - select << "SELECT id, first_name, last_name, email, pubkey, created, email_checked, disabled, group_id FROM " << getTableName() + select << "SELECT id, first_name, last_name, email, username, pubkey, created, email_checked, disabled, group_id FROM " << getTableName() << " where " << fieldName << " LIKE ?"; @@ -130,7 +130,7 @@ namespace model { } // typedef Poco::Tuple, int> UserTuple; - select << "SELECT id, first_name, last_name, email, pubkey, created, email_checked, disabled, group_id FROM " << getTableName() + select << "SELECT id, first_name, last_name, email, username, pubkey, created, email_checked, disabled, group_id FROM " << getTableName() << " where " << fieldNames[0] << " LIKE ?"; if (conditionType == MYSQL_CONDITION_AND) { for (int i = 1; i < fieldNames.size(); i++) { @@ -242,8 +242,8 @@ namespace model { auto session = cm->getConnection(CONNECTION_MYSQL_LOGIN_SERVER); Poco::Data::Statement update(session); - update << "UPDATE users SET first_name = ?, last_name = ?, disabled = ?, language = ? where id = ?;", - use(mFirstName), use(mLastName), use(mDisabled), use(mLanguageKey), use(mID); + update << "UPDATE users SET first_name = ?, last_name = ?, username = ?, disabled = ?, language = ? where id = ?;", + use(mFirstName), use(mLastName), use(mUsername), use(mDisabled), use(mLanguageKey), use(mID); try { @@ -301,7 +301,7 @@ namespace model { sodium_bin2hex(*email_hash, crypto_generichash_BYTES + 1, mEmailHash.value().content().data(), mEmailHash.value().content().size()); } - + ss << mUsername << std::endl; ss << mFirstName << " " << mLastName << " <" << mEmail << ">" << std::endl; ss << "password hash: " << mPasswordHashed << std::endl; ss << "public key: " << (char*)*pubkeyHex << std::endl; @@ -338,7 +338,8 @@ namespace model { if (!mEmailHash.isNull()) { sodium_bin2hex(*email_hash, crypto_generichash_BYTES + 1, mEmailHash.value().content().data(), mEmailHash.value().content().size()); } - + + ss << "" << mUsername << "
"; ss << "" << mFirstName << " " << mLastName << " <" << mEmail << ">" << "
"; ss << "public key: " << (char*)*pubkeyHex << "
"; ss << "email hash: " << (char*)*email_hash << "
"; @@ -371,6 +372,17 @@ namespace model { return pubkeyHexString; } + MemoryBin* User::getPublicKeyCopy() const + { + SHARED_LOCK; + auto mm = MemoryManager::getInstance(); + auto public_key_size = getPublicKeySize(); + if (!public_key_size) return nullptr; + auto pubkey = mm->getFreeMemory(getPublicKeySize()); + memcpy(*pubkey, getPublicKey(), public_key_size); + return pubkey; + } + std::string User::getPrivateKeyEncryptedHex() const { std::shared_lock _lock(mSharedMutex); @@ -400,6 +412,7 @@ namespace model { userObj.set("first_name", mFirstName); userObj.set("last_name", mLastName); userObj.set("email", mEmail); + userObj.set("username", mUsername); //userObj.set("state", userStateToString(mState)); auto createTimeStamp = mCreated.timestamp(); diff --git a/src/cpp/model/table/User.h b/src/cpp/model/table/User.h index 5c4609b02..876803cf8 100644 --- a/src/cpp/model/table/User.h +++ b/src/cpp/model/table/User.h @@ -28,7 +28,7 @@ namespace model { USER_FIELDS_LANGUAGE }; - typedef Poco::Tuple, Poco::DateTime, int, int, int> UserTuple; + typedef Poco::Tuple, Poco::DateTime, int, int, int> UserTuple; class User : public ModelBase { @@ -56,10 +56,13 @@ namespace model { inline const std::string getEmail() const { SHARED_LOCK; return mEmail; } inline const std::string getFirstName() const { SHARED_LOCK; return mFirstName; } inline const std::string getLastName() const { SHARED_LOCK; return mLastName; } + inline const std::string getUsername() const { SHARED_LOCK; return mUsername; } inline std::string getNameWithEmailHtml() const { SHARED_LOCK; return mFirstName + " " + mLastName + " <" + mEmail + ">"; } inline const Poco::UInt64 getPasswordHashed() const { SHARED_LOCK; return mPasswordHashed; } + inline int getGroupId() const { SHARED_LOCK; return mGroupId; } inline RoleType getRole() const { SHARED_LOCK; if (mRole.isNull()) return ROLE_NONE; return static_cast(mRole.value()); } inline const unsigned char* getPublicKey() const { SHARED_LOCK; if (mPublicKey.isNull()) return nullptr; return mPublicKey.value().content().data(); } + MemoryBin* getPublicKeyCopy() const; inline size_t getPublicKeySize() const { SHARED_LOCK; if (mPublicKey.isNull()) return 0; return mPublicKey.value().content().size(); } std::string getPublicKeyHex() const; std::string getPrivateKeyEncryptedHex() const; @@ -71,12 +74,12 @@ namespace model { inline bool isEmailChecked() const { SHARED_LOCK; return mEmailChecked; } inline const std::string getLanguageKey() const { SHARED_LOCK; return mLanguageKey; } inline bool isDisabled() const { SHARED_LOCK; return mDisabled; } - inline int getGroupId() const { SHARED_LOCK; return mGroupId; } // default setter unlocked void setEmail(const std::string& email); inline void setFirstName(const std::string& first_name) { UNIQUE_LOCK; mFirstName = first_name; } inline void setLastName(const std::string& last_name) { UNIQUE_LOCK; mLastName = last_name; } + inline void setUsername(const std::string& username) { UNIQUE_LOCK; mUsername = username; } inline void setPasswordHashed(const Poco::UInt64& passwordHashed) { UNIQUE_LOCK; mPasswordHashed = passwordHashed; } void setPublicKey(const unsigned char* publicKey); //! \brief set encrypted private key @@ -85,6 +88,7 @@ namespace model { inline void setEmailChecked(bool emailChecked) { UNIQUE_LOCK; mEmailChecked = emailChecked; } inline void setLanguageKey(const std::string& languageKey) { UNIQUE_LOCK; mLanguageKey = languageKey; } inline void setDisabled(bool disabled) { UNIQUE_LOCK; mDisabled = disabled; } + inline void setGroupId(int groupId) { UNIQUE_LOCK; mGroupId = groupId; } Poco::JSON::Object getJson(); @@ -100,6 +104,7 @@ namespace model { std::string mEmail; std::string mFirstName; std::string mLastName; + std::string mUsername; Poco::UInt64 mPasswordHashed;