diff --git a/community_server/.gitmodules b/community_server/.gitmodules index ed2d79ec1..e7045e507 100644 --- a/community_server/.gitmodules +++ b/community_server/.gitmodules @@ -1,6 +1,3 @@ -[submodule "mithril_client"] - path = mithril_client - url = git@github.com:gradido/gradido_mithrilJS_client.git [submodule "src/protobuf"] path = src/protobuf url = git@github.com:gradido/gradido_protocol.git \ No newline at end of file diff --git a/community_server/composer.json b/community_server/composer.json index 784c6d123..6a5700b0d 100644 --- a/community_server/composer.json +++ b/community_server/composer.json @@ -6,7 +6,7 @@ "license": "MIT", "require": { "php": ">=5.6", - "cakephp/cakephp": "3.8.*", + "cakephp/cakephp": "3.9.*", "cakephp/migrations": "^2.0.0", "cakephp/plugin-installer": "^1.0", "datto/json-rpc": "^6.0", diff --git a/community_server/config/routes.php b/community_server/config/routes.php index ec3b5fd27..f92b66b8b 100644 --- a/community_server/config/routes.php +++ b/community_server/config/routes.php @@ -57,12 +57,12 @@ Router::scope('/', function (RouteBuilder $routes) { $csrf->whitelistCallback(function ($request) { // Skip token check for API URLs. //die($request->getParam('controller')); - $whitelist = ['JsonRequestHandler', 'ElopageWebhook']; + $whitelist = ['JsonRequestHandler', 'ElopageWebhook', 'AppRequests']; $ajaxWhitelist = ['TransactionSendCoins', 'TransactionCreations']; - + foreach($whitelist as $entry) { if($request->getParam('controller') === $entry) { - if($entry == 'ElopageWebhook') { + if($entry == 'ElopageWebhook' || $entry == 'AppRequests') { return true; } if($request->clientIp() == '127.0.0.1' || $request->clientIp() == 'localhost') { @@ -110,7 +110,10 @@ Router::scope('/', function (RouteBuilder $routes) { */ //$routes->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']); $routes->connect('/', ['controller' => 'Dashboard', 'action' => 'index']); + //$routes->connect('/client', ['controller' => 'Pages', 'action' => 'display', 'js']); $routes->connect('/server', ['controller' => 'Dashboard', 'action' => 'serverIndex']); + $routes->connect('/client', ['controller' => 'Pages', 'action' => 'display', 'vue']); + $routes->connect('/vue-dev', ['controller' => 'Pages', 'action' => 'display', 'vue-dev']); //$routes->connect('/', 'https://gradido2.dario-rekowski.de/account', array('status' => 303)); /** diff --git a/community_server/docu/community-server.api.md b/community_server/docu/community-server.api.md new file mode 100644 index 000000000..6032ce0a2 --- /dev/null +++ b/community_server/docu/community-server.api.md @@ -0,0 +1,132 @@ + +# community server api + +In this examples I assume that you use gradido with docker-compose build on your local maschine + +## Konto Overview +return current account balance + +GET http://localhost/state-balances/ajaxGetBalance/-127182 + +If session is valid, return: +```json +{"state":"success","balance":174500} +``` +- balance: Gradido Cent, 4 Nachkommastellen (2 Reserve), 174500 = 17,45 GDD + +## List Transactions +List all transactions from logged in user, currently without paging +Ajax: +GET http://localhost/state-balances/ajaxListTransactions/-127182/ +or +GET http://localhost/state-balances/ajaxListTransactions/-127182/DESC +to get transaction in descending order + +Antwort: +Wenn alles okay: +```json +{"state":"success", "transactions": + [ + { + "name": "Max Mustermann", + "email": "Maxim Mustermann", + "type": "send", + "transaction_id": 2, + "date": "2021-02-19T13:25:36+00:00", + "balance": 1920000, + "memo": "a piece of cake :)", + "pubkey": "038a6f93270dc57b91d76bf110ad3863fcb7d1b08e7692e793fcdb4467e5b6a7" + } + ], + "transactionExecutingCount": 0, + "count": 1, + "gdtSum": 0, + "timeUsed": 0.04562687873840332 +} +``` + +- name: name of other involved party or empty if unknown (if other party don't belong to group) + - if type is send, name is name of receiver + - if type is receive, name is name of sender + - if type is creation currently I use a static string ("Gradido Akademie) +- email: optional, only if type is send or receive and other user is known +- pubkey: optional, only if type is send or receive and other user isn't known +- type: type of transaction + - creation: user has get gradidos created + - send: user has send another user gradidos + - receiver: user has received gradidos from another user +- transaction_id: id of transaction in db, in stage2 also the hedera sequence number of transaction +- date: date of ordering transaction (booking date) +- balance: Gradido Cent, 4 Nachkommastellen (2 Reserve), 1920000 = 192,00 GDD +- memo: Details about transaction +- pubkey: optional, if other party isn't known, hexadecimal representation of 32 Byte public key of user [0-9a-f] + +- transactionExecutingCount: how many transaction for this user currently pending and waiting for signing +- count: sum of finished transactions user is involved +- gdtSum: sum of gdt of user in cent with 2 places (Nachkommastellen) +- timeUsed: time used for getting data from db in seconds, only for analyse backend performance + +## Creation Transaction +Make a creation transaction +With new Option set in Login-Server: +```ini +unsecure.allow_auto_sign_transactions = 1 +``` +transactions can be auto-signed directly with handing in transaction. +Normally a forwarding to login-server check transactions side is neccessary to minimize security risks. + +POST http://localhost/transaction-creations/ajaxCreate +```json +{ + "session_id" : -127182, + "email": "max.musterman@gmail.de", + "amount": 10000000, + "target_date":"2021-02-19T13:25:36+00:00", + "memo":"AGE", + "auto_sign": true +} +``` +return if everything is ok: +```json +{"state":"success", "timeUsed": 0.0122} +``` +- timeUsed: time used for getting data from db in seconds, only for analyse backend performance + +## Send Coins Transaction +Make a simple GDD Transaction, send Coins from one user to other. +With new Option set in Login-Server: +```ini +unsecure.allow_auto_sign_transactions = 1 +``` +transactions can be auto-signed directly with handing in transaction. +Normally a forwarding to login-server check transactions side is neccessary to minimize security risks. + +POST http://localhost/transaction-send-coins/ajaxCreate +```json +{ + "session_id" : -127182, + "amount": 2000000, + "email": "max.musterman@gmail.de", + "memo":"Thank you :)", + "auto_sign": true +} +``` +- amout: amount to transfer, 2000000 = 200,00 GDD +- email: receiver email address, must be differ from user email +- memo: Details about transaction +- auto_sign: set to true to directly sign transaction if unsecure.allow_auto_sign_transactions = 1 is set + +return if everything is ok: +```json +{"state":"success", "timeUsed": 0.0122} +``` +- timeUsed: time used for getting data from db in seconds, only for analyse backend performance + +Than the transaction was created on community server, send to login-server, signed (if unsecure.allow_auto_sign_transactions = 1 and auto_sign = true) +and send back to community server and put into db. +After you get this answear you see the new transaction if you list transactions or call for the balance. + +Without auto-sign the transaction is pending on login-server and waits for the user to review it at +http://localhost/account/checkTransactions + + diff --git a/community_server/parse_proto.sh b/community_server/parse_proto.sh index 576b99691..e6ccb451f 100755 --- a/community_server/parse_proto.sh +++ b/community_server/parse_proto.sh @@ -16,5 +16,5 @@ #PHP_PLUGIN="$(which grpc_php_plugin)" #protoc --proto_path=./src/protobuf/gradido --php_out=./src/ --grpc_out=./src/ --plugin=protoc-gen-grpc=$PHP_PLUGIN ./src/protobuf/gradido/*.proto -protoc --proto_path=./src/protobuf/gradido --php_out=./src/ ./src/protobuf/gradido/*.proto +protoc --proto_path=./src/protobuf --php_out=./src/Model/Messages ./src/protobuf/gradido/*.proto diff --git a/community_server/skeema/gradido_community/address_types.sql b/community_server/skeema/gradido_community/address_types.sql new file mode 100644 index 000000000..cb5b1b943 --- /dev/null +++ b/community_server/skeema/gradido_community/address_types.sql @@ -0,0 +1,7 @@ +CREATE TABLE `address_types` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL, + `text` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + diff --git a/community_server/skeema/gradido_community/insert/address_types.sql b/community_server/skeema/gradido_community/insert/address_types.sql new file mode 100644 index 000000000..2a6dc00a5 --- /dev/null +++ b/community_server/skeema/gradido_community/insert/address_types.sql @@ -0,0 +1,2 @@ +INSERT INTO `address_types` (`id`, `name`, `text`) VALUES +(1, 'user main', 'user main address'); diff --git a/community_server/skeema/gradido_community/insert/insert_transaction_types.sql b/community_server/skeema/gradido_community/insert/insert_transaction_types.sql index 9dbf5b93f..02ef8374a 100644 --- a/community_server/skeema/gradido_community/insert/insert_transaction_types.sql +++ b/community_server/skeema/gradido_community/insert/insert_transaction_types.sql @@ -1,5 +1,10 @@ - INSERT INTO `transaction_types` (`id`, `name`, `text`) VALUES -(1, 'creation', 'Aktives oder Bedingungsloses Grundeinkommen.'), -(2, 'transfer', 'Einfache Überweisung'); +(1, 'creation', 'create new gradidos for member and also for group (in development)'), +(2, 'transfer', 'send gradidos from one member to another, also cross group transfer'), +(3, 'group create', 'create a new group, trigger creation of new hedera topic and new blockchain on node server'), +(4, 'group add member', 'add user to a group or move if he was already in a group'), +(5, 'group remove member', 'remove user from group, maybe he was moved elsewhere'), +(6, 'hedera topic create', 'create new topic on hedera'), +(7, 'hedera topic send message', 'send consensus message over hedera topic'), +(8, 'hedera account create', 'create new account on hedera for holding some founds with unencrypted keys'); diff --git a/community_server/skeema/gradido_community/state_group_addresses.sql b/community_server/skeema/gradido_community/state_group_addresses.sql index 1d256f3e0..698a79b98 100644 --- a/community_server/skeema/gradido_community/state_group_addresses.sql +++ b/community_server/skeema/gradido_community/state_group_addresses.sql @@ -3,5 +3,6 @@ CREATE TABLE `state_group_addresses` ( `group_id` int(10) unsigned NOT NULL, `public_key` binary(32) NOT NULL, `address_type_id` int(10) unsigned NOT NULL, - PRIMARY KEY (`id`) + PRIMARY KEY (`id`), + UNIQUE(`public_key`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/community_server/skeema/gradido_community/state_user_transactions.sql b/community_server/skeema/gradido_community/state_user_transactions.sql index 272552845..5e01cac4a 100644 --- a/community_server/skeema/gradido_community/state_user_transactions.sql +++ b/community_server/skeema/gradido_community/state_user_transactions.sql @@ -4,6 +4,6 @@ CREATE TABLE `state_user_transactions` ( `transaction_id` int UNSIGNED NOT NULL, `transaction_type_id` int UNSIGNED NOT NULL, `balance` bigint(20) DEFAULT 0, - `balance_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `balance_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/community_server/skeema/gradido_community/transaction_group_addaddress.sql b/community_server/skeema/gradido_community/transaction_group_addaddress.sql index dceac273c..cb21ed99f 100644 --- a/community_server/skeema/gradido_community/transaction_group_addaddress.sql +++ b/community_server/skeema/gradido_community/transaction_group_addaddress.sql @@ -1,7 +1,9 @@ CREATE TABLE `transaction_group_addaddress` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `transaction_id` int(10) unsigned NOT NULL, - `address_type_id` int(10) unsigned NOT NULL, + `id` int unsigned NOT NULL AUTO_INCREMENT, + `transaction_id` int unsigned NOT NULL, + `address_type_id` int unsigned NOT NULL, + `remove_from_group` BOOLEAN DEFAULT FALSE, `public_key` binary(32) NOT NULL, + `state_user_id` int unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/community_server/skeema/gradido_community/transaction_send_coins.sql b/community_server/skeema/gradido_community/transaction_send_coins.sql index f57a2175e..49423fdbc 100644 --- a/community_server/skeema/gradido_community/transaction_send_coins.sql +++ b/community_server/skeema/gradido_community/transaction_send_coins.sql @@ -1,9 +1,10 @@ CREATE TABLE `transaction_send_coins` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `transaction_id` int(10) unsigned NOT NULL, - `state_user_id` int(10) unsigned NOT NULL, + `sender_public_key` binary(32) NOT NULL, + `state_user_id` int(10) unsigned DEFAULT 0, `receiver_public_key` binary(32) NOT NULL, - `receiver_user_id` int(10) unsigned NOT NULL, + `receiver_user_id` int(10) unsigned DEFAULT 0, `amount` bigint(20) NOT NULL, `sender_final_balance` bigint(20) NOT NULL, PRIMARY KEY (`id`) diff --git a/community_server/skeema/gradido_community/transaction_types.sql b/community_server/skeema/gradido_community/transaction_types.sql index 87492e1fe..a3e6779d9 100644 --- a/community_server/skeema/gradido_community/transaction_types.sql +++ b/community_server/skeema/gradido_community/transaction_types.sql @@ -1,6 +1,6 @@ CREATE TABLE `transaction_types` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `name` varchar(24) COLLATE utf8mb4_unicode_ci NOT NULL, + `name` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL, `text` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/community_server/skeema/gradido_community/transactions.sql b/community_server/skeema/gradido_community/transactions.sql index 0e8c00ce6..566cfc048 100644 --- a/community_server/skeema/gradido_community/transactions.sql +++ b/community_server/skeema/gradido_community/transactions.sql @@ -2,7 +2,7 @@ CREATE TABLE `transactions` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `state_group_id` int(10) unsigned DEFAULT NULL, `transaction_type_id` int(10) unsigned NOT NULL, - `tx_hash` binary(32) DEFAULT NULL, + `tx_hash` binary(48) DEFAULT NULL, `memo` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `received` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) diff --git a/community_server/src/Controller/AddressTypesController.php b/community_server/src/Controller/AddressTypesController.php index 348a8e9e6..9b13a7720 100644 --- a/community_server/src/Controller/AddressTypesController.php +++ b/community_server/src/Controller/AddressTypesController.php @@ -2,7 +2,6 @@ namespace App\Controller; use App\Controller\AppController; -use Cake\I18n\Number; /** * AddressTypes Controller @@ -35,7 +34,7 @@ class AddressTypesController extends AppController public function view($id = null) { $addressType = $this->AddressTypes->get($id, [ - 'contain' => ['StateGroupAddresses', 'TransactionGroupAddaddress'] + 'contain' => ['StateGroupAddresses', 'TransactionGroupAddaddress'], ]); $this->set('addressType', $addressType); @@ -71,7 +70,7 @@ class AddressTypesController extends AppController public function edit($id = null) { $addressType = $this->AddressTypes->get($id, [ - 'contain' => [] + 'contain' => [], ]); if ($this->request->is(['patch', 'post', 'put'])) { $addressType = $this->AddressTypes->patchEntity($addressType, $this->request->getData()); diff --git a/community_server/src/Controller/AppController.php b/community_server/src/Controller/AppController.php index fb0577034..b9522c2e7 100644 --- a/community_server/src/Controller/AppController.php +++ b/community_server/src/Controller/AppController.php @@ -91,12 +91,11 @@ class AppController extends Controller $stateBalancesTable = TableRegistry::getTableLocator()->get('stateBalances'); $stateBalanceQuery = $stateBalancesTable ->find('all') - ->select('amount') ->contain(false) ->where(['state_user_id' => $state_user_id]); if ($stateBalanceQuery->count() == 1) { //var_dump($stateBalanceEntry->first()); - $session->write('StateUser.balance', $stateBalanceQuery->first()->amount); + $session->write('StateUser.balance', $stateBalanceQuery->first()->decay); //echo "stateUser.balance: " . $session->read('StateUser.balance'); } } @@ -147,6 +146,10 @@ class AppController extends Controller //$this->Cookie->configKey('User', 'encryption', false); if(!$session_id) { $session_id = intval($this->request->getCookie('GRADIDO_LOGIN', '')); + // TODO: This is unclear if correct + if($session_id == 0 && $session->check('session_id')) { + $session_id = intval($session->read('session_id')); + } } $ip = $this->request->clientIp(); if (!$session->check('client_ip')) { @@ -158,14 +161,16 @@ class AppController extends Controller if ($session_id != 0) { $userStored = $session->read('StateUser'); - - $transactionPendings = $session->read('Transaction.pending'); - $transactionExecutings = $session->read('Transaction.executing'); - + + $transactionPendings = $session->read('Transactions.pending'); + $transactionExecutings = $session->read('Transactions.executing'); + $transaction_can_signed = $session->read('Transactions.can_signed'); + if ($session->read('session_id') != $session_id || ( $userStored && (!isset($userStored['id']) || !$userStored['email_checked'])) || intval($transactionPendings) > 0 || - intval($transactionExecutings) > 0) { + intval($transactionExecutings) > 0 || + intval($transaction_can_signed > 0)) { $http = new Client(); try { @@ -187,11 +192,13 @@ class AppController extends Controller $session->write('StateUser.' . $key, $value); } //var_dump($json); - $transactionPendings = $json['Transaction.pending']; - $transactionExecuting = $json['Transaction.executing']; + $transactionPendings = $json['Transactions.pending']; + $transactionExecuting = $json['Transactions.executing']; + $transaction_can_signed = $json['Transactions.can_signed']; //echo "read transaction pending: $transactionPendings
"; - $session->write('Transaction.pending', $transactionPendings); - $session->write('Transaction.executing', $transactionExecuting); + $session->write('Transactions.pending', $transactionPendings); + $session->write('Transactions.executing', $transactionExecuting); + $session->write('Transactions.can_signed', $transaction_can_signed); $session->write('session_id', $session_id); $stateUserTable = TableRegistry::getTableLocator()->get('StateUsers'); @@ -200,12 +207,15 @@ class AppController extends Controller $stateUserQuery = $stateUserTable ->find('all') ->where(['public_key' => $public_key_bin]) - ->contain(['StateBalances']); + ->contain('StateBalances', function ($q) { + return $q->order(['record_date' => 'DESC']) + ->limit(1); + }); if ($stateUserQuery->count() == 1) { $stateUser = $stateUserQuery->first(); if ($stateUser->first_name != $json['user']['first_name'] || $stateUser->last_name != $json['user']['last_name'] || - $stateUser->disabled != intval($json['user']['disabled']) || + $stateUser->disabled != $json['user']['disabled'] || //$stateUser->username != $json['user']['username'] || // -> throws error $stateUser->email != $json['user']['email'] @@ -221,7 +231,8 @@ class AppController extends Controller } //var_dump($stateUser); if (count($stateUser->state_balances) > 0) { - $session->write('StateUser.balance', $stateUser->state_balances[0]->amount); + + $session->write('StateUser.balance', $stateUser->state_balances[0]->decay); } $session->write('StateUser.id', $stateUser->id); //echo $stateUser['id']; @@ -258,6 +269,9 @@ class AppController extends Controller $this->Flash->error(__('Konto ist nicht aktiviert!')); } //die(json_encode($json)); + if(preg_match('/client ip/', $json['msg'])) { + return $this->redirect($this->loginServerUrl . 'account/error500/ipError', 303); + } return $this->redirect($this->loginServerUrl . 'account/', 303); } } diff --git a/community_server/src/Controller/AppRequestsController.php b/community_server/src/Controller/AppRequestsController.php new file mode 100644 index 000000000..c36ddf161 --- /dev/null +++ b/community_server/src/Controller/AppRequestsController.php @@ -0,0 +1,61 @@ +loadComponent('JsonRequestClient'); + $this->loadComponent('JsonRpcRequestClient'); + //$this->Auth->allow(['add', 'edit']); + $this->Auth->allow('index'); + } + + + public function index() + { + if($this->request->is('get')) { + $method = $this->request->getQuery('method'); + switch($method) { + + } + return $this->returnJson(['state' => 'error', 'msg' => 'unknown method for get', 'details' => $method]); + } + else if($this->request->is('post')) { + $jsonData = $this->request->input('json_decode'); + //var_dump($jsonData); + if($jsonData == NULL || !isset($jsonData->method)) { + return $this->returnJson(['state' => 'error', 'msg' => 'parameter error']); + } + $method = $jsonData->method; + + switch($method) { + + } + return $this->returnJson(['state' => 'error', 'msg' => 'unknown method for post', 'details' => $method]); + } + return $this->returnJson(['state' => 'error', 'msg' => 'no post or get']); + } + + private function acquireAccessToken($session_id) + { + + } + +} + \ No newline at end of file diff --git a/community_server/src/Controller/Component/JsonRequestClientComponent.php b/community_server/src/Controller/Component/JsonRequestClientComponent.php index 8a071b70b..4132894eb 100644 --- a/community_server/src/Controller/Component/JsonRequestClientComponent.php +++ b/community_server/src/Controller/Component/JsonRequestClientComponent.php @@ -41,6 +41,11 @@ class JsonRequestClientComponent extends Component } + public function findePublicKeyForEmailHash($emailHash) { + //'ask' = ['account_publickey' => ''] + $results = $this->sendRequestLoginServerNeighbors(json_encode(['ask' => ['account_publickey' => $emailHash]]), 'search'); + } + public function getRunningUserTasks($email) { if($email == "") { @@ -121,6 +126,39 @@ class JsonRequestClientComponent extends Component return ['state' => 'success', 'data' => $json]; } + public function sendRequestLoginServerNeighbors($transactionBody, $url) { + + $http = new Client(); + if(!Configure::check('NeighborLoginServers')) { + return ['state' => 'warning', 'msg' => 'no neighbor server configured']; + } + $nServers = Configure::read('NeighborLoginServers'); + $results = ['errors' => [], 'data' => []]; + foreach($nServers as $nServer) { + $full_url = $nServer['host'] . ':' . $nServer['port'] . '/' . $url; + $response = $http->post($full_url, $transactionBody, ['type' => 'json']); + $responseStatus = $response->getStatusCode(); + if($responseStatus != 200) { + $results['errors'][] = [ + 'state' => 'error', + 'type' => 'request error', + 'msg' => 'server response status code isn\'t 200', + 'details' => $responseStatus, + 'fullUrl' => $full_url + ]; + continue; + } + $json = $response->getJson(); + if($json == null) { + //$responseType = $response->getType(); + $results['errors'][] = ['state' => 'error', 'type' => 'request error', 'msg' => 'server response isn\'t valid json']; + continue; + } + $results['data'][] = $json; + } + return $results; + } + static public function getLoginServerUrl() { $loginServer = Configure::read('LoginServer'); diff --git a/community_server/src/Controller/Component/JsonRpcRequestClientComponent.php b/community_server/src/Controller/Component/JsonRpcRequestClientComponent.php index d5ca72808..579eafb22 100644 --- a/community_server/src/Controller/Component/JsonRpcRequestClientComponent.php +++ b/community_server/src/Controller/Component/JsonRpcRequestClientComponent.php @@ -41,7 +41,15 @@ class JsonRpcRequestClientComponent extends Component public function sendRequest($message) { $http = new Client(); - $response = $http->post($this->getGradidoNodeUrl(), $message, ['type' => 'json']); + try { + $url = $this->pickGradidoNodeUrl(); + if(is_array($url)) { + return $url; + } + $response = $http->post($url, $message, ['type' => 'json']); + } catch(Exception $e) { + return ['state' => 'error', 'type' => 'http exception', 'details' => $e->getMessage()]; + } $responseStatus = $response->getStatusCode(); if($responseStatus != 200) { return ['state' => 'error', 'type' => 'request error', 'msg' => 'server response status code isn\'t 200', 'details' => $responseStatus]; @@ -55,14 +63,18 @@ class JsonRpcRequestClientComponent extends Component //$responseType = $response->getType(); return ['state' => 'error', 'type' => 'request error', 'msg' => 'server response isn\'t valid json']; } - return $json; + return $json['result']; //return ['state' => 'success', 'data' => $json]; } - static public function getGradidoNodeUrl() + static public function pickGradidoNodeUrl() { - $gradidoNode = Configure::read('GradidoNode'); - return $gradidoNode['host'] . ':' . $gradidoNode['port']; + $gradidoNodes = Configure::read('GradidoNode'); + if(count($gradidoNodes) == 0) { + return ['state' => 'error', 'msg' => 'no gradido nodes in config']; + } + $i = rand(0, count($gradidoNodes)-1); + return $gradidoNodes[$i]['host'] . ':' . $gradidoNodes[$i]['port']; } diff --git a/community_server/src/Controller/ErrorController.php b/community_server/src/Controller/ErrorController.php index 43bd2fb52..c5421a24d 100644 --- a/community_server/src/Controller/ErrorController.php +++ b/community_server/src/Controller/ErrorController.php @@ -54,7 +54,7 @@ class ErrorController extends AppController public function beforeRender(Event $event) { parent::beforeRender($event); - + $this->RequestHandler->renderAs($this, 'json'); $this->viewBuilder()->setTemplatePath('Error'); } diff --git a/community_server/src/Controller/JsonRequestHandlerController.php b/community_server/src/Controller/JsonRequestHandlerController.php index e5823397e..ba68f2b5a 100644 --- a/community_server/src/Controller/JsonRequestHandlerController.php +++ b/community_server/src/Controller/JsonRequestHandlerController.php @@ -10,6 +10,7 @@ use Cake\Core\Configure; use Model\Transactions\TransactionTransfer; use Model\Transactions\Transaction; +use Model\Transactions\Record; /*! * @author: Dario Rekowski# * @@ -24,6 +25,7 @@ class JsonRequestHandlerController extends AppController { { parent::initialize(); $this->loadComponent('JsonRequestClient'); + $this->loadComponent('JsonRpcRequestClient'); //$this->Auth->allow(['add', 'edit']); $this->Auth->allow('index'); } @@ -58,28 +60,277 @@ class JsonRequestHandlerController extends AppController { case 'checkUser': return $this->checkUser($jsonData->email, $jsonData->last_name); case 'getUsers' : return $this->getUsers($jsonData->page, $jsonData->limit); case 'getUserBalance': return $this->getUserBalance($jsonData->email, $jsonData->last_name); - case 'errorInTransaction': return $this->errorInTransaction($jsonData->created, $jsonData->transactionGenericHash, $jsonData->error, $jsonData->errorMessage); + case 'errorInTransaction': return $this->errorInTransaction($jsonData); + case 'updateReadNode': return $this->updateReadNode(); + case 'addUser' : return $this->addUser($jsonData->user); } return $this->returnJson(['state' => 'error', 'msg' => 'unknown method for post', 'details' => $method]); } return $this->returnJson(['state' => 'error', 'msg' => 'no post or get']); } - + + private function addUser($newUser) + { + $stateUsersTable = TableRegistry::getTableLocator()->get('StateUsers'); + $entity = $stateUsersTable->newEntity(); + $required_fields = ['first_name', 'last_name', 'email', 'public_key', 'disabled']; + foreach($required_fields as $required_field) { + if(!isset($newUser->$required_field)) { + return $this->returnJson(['state' => 'error', 'msg' => 'missing required field in addUser', 'details' => $required_field]); + } + if('public_key' == $required_field) { + $entity->$required_field = hex2bin($newUser->public_hex); + } else { + $entity->$required_field = $newUser->$required_field; + } + } + if($stateUsersTable->save($entity)) { + return $this->returnJson(['state' => 'success']); + } else { + return $this->returnJson(['state' => 'error', 'msg' => 'error saving state_user', 'details' => $entity->getErrors()]); + } + } + + // Called from login server like a cron job every 10 minutes or after sending transaction to hedera + private function updateReadNode() + { + $this->autoRender = false; + $response = $this->response->withType('application/json'); + + $transactionsTable = TableRegistry::getTableLocator()->get('Transactions'); + $last_transaction_query = $transactionsTable->find('all')->order(['id' => 'DESC']); + $last_transaction_id = 0; + if(!$last_transaction_query->isEmpty()) { + $last_transaction_id = $last_transaction_query->first()->id; + } + $last_known_sequence_number = $last_transaction_id; + + if($last_transaction_query->count() < $last_transaction_id) { + $last_transaction_id = $last_transaction_query->count(); + } + //$last_transaction_id = 0; + + + $group_alias = Configure::read('GroupAlias'); + $result = (array)$this->JsonRpcRequestClient->request('getTransactions', ['groupAlias' => $group_alias, 'lastKnownSequenceNumber' => $last_transaction_id]); + if(isset($result['state']) && $result['state'] == 'error') { + return $this->returnJson(['state' => 'error', 'msg' => 'jsonrpc error', 'details' => ['return' => $result, 'groupAlias' => $group_alias]]); + } + /* example + $result = json_decode("[ + { + \"record_type\":\"GRADIDO_TRANSACTION\", + \"transaction\":{ + \"version_number\":1, + \"signature\":{ + \"pubkey\":\"2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71b\", + \"signature\":\"aed6725baacabf903e51f92503d49fa7e6b93c6402d56d9e3784be9a3366a77459213d858af46b579287aba8b1b63d206febce18bc80cec6fa63da6289e56403\" + }, + \"signature_count\":1, + \"hedera_transaction\":{ + \"consensusTimestamp\":{ + \"seconds\":1604392811, + \"nanos\":172812 + }, + \"runningHash\":\"f9ccf04137be418c3117a28bb5add6dced9745bcab74b7a2f46c182c8c98eeabf0127c131d15ebea7d0ac376f5d2de45\", + \"sequenceNumber\":94, + \"runningHashVersion\":3 + }, + \"transaction_type\":\"ADD_USER\", + \"add_user\":{ + \"user\":\"2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71b\" + }, + \"result\":\"result\", + \"parts\":1, + \"memo\":\"\" + } + }, + { + \"record_type\":\"GRADIDO_TRANSACTION\", + \"transaction\":{ + \"version_number\":1, + \"signature\":{ + \"pubkey\":\"8190bda585ee5f1d9fbf7d06e81e69ec18e13376104cff54b7457eb7d3ef710d\", + \"signature\":\"3134adcd6cbccee17c2db398f91b6b6bdd098b6306fb2fa213eb9eb5a322af9078acca4d8b0383d4e906f3139eb3369e7c1ef0f3ac5fec724be0d085ba44af0b\" + }, + \"signature_count\":2, + \"hedera_transaction\":{ + \"consensusTimestamp\":{ + \"seconds\":1604392886, + \"nanos\":1528 + }, + \"runningHash\":\"e1df5526331e3def11d6b652b8f248d20c250739b6eb98f1fe7b338901753d9d573a14601ba84f61318a48940b3c237a\", + \"sequenceNumber\":95, + \"runningHashVersion\":3 + }, + \"transaction_type\":\"ADD_USER\", + \"add_user\":{ + \"user\":\"8190bda585ee5f1d9fbf7d06e81e69ec18e13376104cff54b7457eb7d3ef710d\" + }, + \"result\":\"result\", + \"parts\":2, + \"memo\":\"\" + } + }, + { + \"record_type\":\"SIGNATURES\", + \"signature\":[ + { + \"pubkey\":\"2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71b\", + \"signature\":\"401717e768617c0f3311931c34a61e66ab362599a0e2a48ae7c4955645aec6573773985dafb84a11bfaf2bc12140c30b2f8c8ee094bc35d609bc56d15b4e9f04\" + } + ] + }, + { + \"record_type\": \"GRADIDO_TRANSACTION\", + \"transaction\":{ + \"version_number\":1, + \"signature\":{ + \"pubkey\":\"2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71b\", + \"signature\":\"99665dee9f2b475e426a2f449d0dae61924f6cf025903666ff72f2c7ef1af27523ebcd5fb684d17813fe7906b2f8cfe5ef4bdbb264ebf3ef80363491d9b86807\" + }, + \"signature_count\":1, + \"hedera_transaction\":{ + \"consensusTimestamp\":{ + \"seconds\":1604392904, + \"nanos\":798541 + }, + \"runningHash\":\"f1fd03610a9788e9bac01e1efb8b99bafae450f9088cb940db954842e0799235c57d842be83d998e6c21786f77f967a7\", + \"sequenceNumber\":96, + \"runningHashVersion\":3 + }, + \"transaction_type\":\"GRADIDO_CREATION\", + \"gradido_creation\":{ + \"user\":\"8190bda585ee5f1d9fbf7d06e81e69ec18e13376104cff54b7457eb7d3ef710d\", + \"new_balance\":10000000, + \"prev_transfer_rec_num\":0, + \"amount\":10000000 + }, + \"result\":\"result\", + \"parts\":1, + \"memo\":\"\" + } + }, + { + \"record_type\": \"GRADIDO_TRANSACTION\", + \"transaction\":{ + \"version_number\":1, + \"signature\":{ + \"pubkey\":\"8190bda585ee5f1d9fbf7d06e81e69ec18e13376104cff54b7457eb7d3ef710d\", + \"signature\":\"90125e0cfce61397d50ed9ba6c5df4cd4e0cf6fee8b10c70fee2898765982570d9a1208c222981429ae3c229e3fd36c2bf2333518cd0a4f0515937822e499d0b\" + }, + \"signature_count\":1, + \"hedera_transaction\":{ + \"consensusTimestamp\":{ + \"seconds\":1604392929, + \"nanos\":52539 + }, + \"runningHash\":\"a4be8f54be4f806b61d31f6bd770d7742822f14f03ffe09c07f08bac3031a06d12de5e38fec5c307149c7faf6e9879b8\", + \"sequenceNumber\":97, + \"runningHashVersion\":3 + }, + \"transaction_type\":\"LOCAL_TRANSFER\", + \"local_transfer\":{ + \"sender\":{ + \"user\":\"8190bda585ee5f1d9fbf7d06e81e69ec18e13376104cff54b7457eb7d3ef710d\", + \"new_balance\":9825500, + \"prev_transfer_rec_num\":0 + }, + \"receiver\":{ + \"user\":\"2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71b\", + \"new_balance\":174500, + \"prev_transfer_rec_num\":0 + }, + \"amount\":174500 + }, + \"result\":\"result\", + \"parts\":1, + \"memo\":\"\" + } + } +]", true);*/ + $part_count = -1; + $temp_record = new Record; + $errors = []; + foreach($result['blocks'] as $_record) { + if(is_string($_record)) continue; + $parse_result = $temp_record->parseRecord($_record); + + if($parse_result == true) { + $sequenceNumber = $temp_record->getSequenceNumber(); + if($part_count == -1) { + $part_count = $temp_record->getPartCount(); + } + $part_count--; + + if($part_count == 0) { + if($sequenceNumber > $last_known_sequence_number) { + $finalize_result = $temp_record->finalize(); + if($finalize_result !== true) { + $errors[] = ['msg' => 'error in finalize', 'record' => $_record, 'details' => $finalize_result, 'sequenceNumber' => $sequenceNumber]; + } + } + + $temp_record = new Record; + $part_count = -1; + } + } else { + $temp_record = new Record; + $part_count = -1; + $errors[] = ['msg' => 'error in parse record', 'record' => $_record, 'details' => $parse_result]; + } + } + if(count($errors)) { + return $this->returnJson(['state' => 'error', 'msg' => 'error in parsing records', 'details' => $errors]); + } + return $this->returnJson(['state' => 'success']); + } + + /* + * payload.set("created", created); + * payload.set("id", task_model->getID()); + * payload.set("type", task_model->getTaskTypeString()); + * payload.set("public_key", user_model->getPublicKeyHex()); + * payload.set("error", error); + * payload.set("errorMessage", errorDetails); + */ //! \param $transactionCreated creation of transaction in timestamp in seconds //! -1 if transaction couldn't decode //! \param $transactionBodyBase64Sha256 generic hash from transaction body serialized and converted to base64 //! using sodium_crypto_generichash to calculate // hash also in base64 format //! \param $error short error name in user language - //! \param $errorDetails more detailed error message in user language - private function errorInTransaction($transactionCreated, $transactionBodyBase64GenericHash, $error, $errorDetails) { - /* - * payload.set("created", created); - * payload.set("id", task_model->getID()); - * payload.set("public_key", user_model->getPublicKeyHex()); - * payload.set("error", error); - * payload.set("errorMessage", errorDetails); - */ + //! \param $errorDetails more detailed error message in user language + private function errorInTransaction($jsonData) { + $stateErrorTable = TableRegistry::getTableLocator()->get('StateErrors'); + $stateUsersTable = TableRegistry::getTableLocator()->get('StateUsers'); + $transactionTypesTable = TableRegistry::getTableLocator()->get('TransactionTypes'); + $stateError = $stateErrorTable->newEntity(); + // + $pubkey = hex2bin($jsonData->public_key); + $user_query = $stateUsersTable->find('all')->select(['id'])->where(['public_key' => $pubkey]); + if($user_query->count() != 1) { + return $this->returnJson(['state' => 'error', 'msg' => 'user not found', 'details' => 'user pubkey hex:' . $jsonData->public_key]); + } + $stateError->state_user_id = $user_query->first()->id; + //$stateError->transaction_type_id + // TODO: + // - show state errors in navi_notify.ctp + $transaction_type_query = $transactionTypesTable->find('all')->select(['id'])->where(['name' => $jsonData->type]); + if($transaction_type_query->count() != 1) { + return $this->returnJson(['state' => 'error', 'msg' => 'transaction type not found', 'details' => 'transaction type name: ' . $jsonData->type]); + } + $stateError->transaction_type_id = $transaction_type_query->first()->id; + $stateError->created = $jsonData->created; + $stateError->message_json = json_encode(['task_id' => $jsonData->id, 'error' => $jsonData->error, 'errorMessage' => $jsonData->errorMessage]); + if(!$stateErrorTable->save($stateError)) { + $this->returnJsonSaveError($stateError, [ + 'state' => 'error', + 'msg' => 'error saving state_error in db', + 'details' => json_encode($stateError->getErrors()) + ]); + } + return $this->returnJson(['state' => 'success']); } private function putTransaction($transactionBase64) { diff --git a/community_server/src/Controller/PagesController.php b/community_server/src/Controller/PagesController.php index 94508ad8b..69f57356b 100644 --- a/community_server/src/Controller/PagesController.php +++ b/community_server/src/Controller/PagesController.php @@ -46,6 +46,7 @@ class PagesController extends AppController */ public function display(...$path) { + $count = count($path); if (!$count) { return $this->redirect('/'); @@ -61,6 +62,16 @@ class PagesController extends AppController if (!empty($path[1])) { $subpage = $path[1]; } + /*$session = $this->getRequest()->getSession(); + $result = $this->requestLogin(); + if($result !== true) { + return $result; + } + $user = $session->read('StateUser'); + $login_server_session = $this->request->getCookie('GRADIDO_LOGIN', '');*/ + if($page == "vue" || $page == "vue-dev") { + $this->viewBuilder()->setLayout(false); + } $this->set(compact('page', 'subpage')); try { diff --git a/community_server/src/Controller/RolesController.php b/community_server/src/Controller/RolesController.php new file mode 100644 index 000000000..80aa493d3 --- /dev/null +++ b/community_server/src/Controller/RolesController.php @@ -0,0 +1,108 @@ +paginate($this->Roles); + + $this->set(compact('roles')); + } + + /** + * View method + * + * @param string|null $id Role id. + * @return \Cake\Http\Response|null + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function view($id = null) + { + $role = $this->Roles->get($id, [ + 'contain' => [], + ]); + + $this->set('role', $role); + } + + /** + * Add method + * + * @return \Cake\Http\Response|null Redirects on successful add, renders view otherwise. + */ + public function add() + { + $role = $this->Roles->newEntity(); + if ($this->request->is('post')) { + $role = $this->Roles->patchEntity($role, $this->request->getData()); + if ($this->Roles->save($role)) { + $this->Flash->success(__('The role has been saved.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('The role could not be saved. Please, try again.')); + } + $this->set(compact('role')); + } + + /** + * Edit method + * + * @param string|null $id Role id. + * @return \Cake\Http\Response|null Redirects on successful edit, renders view otherwise. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function edit($id = null) + { + $role = $this->Roles->get($id, [ + 'contain' => [], + ]); + if ($this->request->is(['patch', 'post', 'put'])) { + $role = $this->Roles->patchEntity($role, $this->request->getData()); + if ($this->Roles->save($role)) { + $this->Flash->success(__('The role has been saved.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('The role could not be saved. Please, try again.')); + } + $this->set(compact('role')); + } + + /** + * Delete method + * + * @param string|null $id Role id. + * @return \Cake\Http\Response|null Redirects to index. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function delete($id = null) + { + $this->request->allowMethod(['post', 'delete']); + $role = $this->Roles->get($id); + if ($this->Roles->delete($role)) { + $this->Flash->success(__('The role has been deleted.')); + } else { + $this->Flash->error(__('The role could not be deleted. Please, try again.')); + } + + return $this->redirect(['action' => 'index']); + } +} diff --git a/community_server/src/Controller/StateBalancesController.php b/community_server/src/Controller/StateBalancesController.php index bddf35125..5d469ca35 100644 --- a/community_server/src/Controller/StateBalancesController.php +++ b/community_server/src/Controller/StateBalancesController.php @@ -2,11 +2,14 @@ namespace App\Controller; use Cake\ORM\TableRegistry; -use App\Controller\AppController; +use Cake\I18n\Time; use Model\Navigation\NaviHierarchy; use Model\Navigation\NaviHierarchyEntry; +use App\Controller\AppController; + + /** * StateBalances Controller * @@ -21,7 +24,7 @@ class StateBalancesController extends AppController { parent::initialize(); //$this->Auth->allow(['add', 'edit']); - $this->Auth->allow(['overview', 'overviewGdt', 'ajaxGetBalance', 'ajaxListTransactions']); + $this->Auth->allow(['overview', 'overviewGdt', 'ajaxListTransactions', 'ajaxGdtOverview', 'ajaxGetBalance', 'ajaxGdtTransactions']); $this->loadComponent('JsonRequestClient'); } /** @@ -38,33 +41,145 @@ class StateBalancesController extends AppController $this->set(compact('stateBalances')); } - - private function updateBalances($state_user_id) + + private function updateBalance($stateUserId) { - $state_balances = $this->StateBalances->find('all')->where(['state_user_id' => $state_user_id]); - if($state_balances->count() == 1) { - $stateUserTransactionsTable = TableRegistry::getTableLocator()->get('StateUserTransactions'); - $state_user_transactions = $stateUserTransactionsTable + $stateUserTransactionsTable = TableRegistry::getTableLocator()->get('StateUserTransactions'); + $transactionsTable = TableRegistry::getTableLocator()->get('Transactions'); + // info: cakephp use lazy loading, query will be executed later only if needed + $state_balances = $this->StateBalances->find('all')->where(['state_user_id' => $stateUserId]); + $state_user_transactions = $stateUserTransactionsTable ->find('all') - ->where(['state_user_id' => $state_user_id]) + ->where(['state_user_id' => $stateUserId]) ->order(['transaction_id ASC']) - ->contain(['']); - if($state_user_transactions->count() == 0){ - return; - } - $last_state_user_transaction = $state_user_transactions->last(); - $last_transaction = $this->StateBalance->newEntity(); - $last_transaction->amount = $last_state_user_transaction->balance; - $last_transaction->record_date = $last_state_user_transaction->balance_date; - // if entrys are nearly the same, we don't need doing anything - if(abs($last_transaction->decay - $state_balances->decay) < 100) { - return; - } - foreach($state_user_transactions as $state_user_transaction) { - - } - + ->contain(false); + + if(!$state_user_transactions) { + //debug($state_user_transactions); + return true; } + + // first: decide what todo + $create_state_balance = false; + $recalculate_state_user_transactions_balance = false; + $clear_state_balance = false; + $update_state_balance = false; + if($state_balances->count() == 0) { + $create_state_balance = true; + } + if($state_balances->count() > 1) { + $clear_state_balance = true; + $create_state_balance = true; + } + if($state_balances->count() == 1) { + if($state_user_transactions->count() == 0){ + $clear_state_balance = true; + } else { + $last_state_user_transaction = $state_user_transactions->last(); + $last_transaction = $this->StateBalances->newEntity(); + $last_transaction->amount = $last_state_user_transaction->balance; + $last_transaction->record_date = $last_state_user_transaction->balance_date; + // if entrys are nearly the same, we don't need doing anything + if(abs($last_transaction->decay - $state_balances->first()->decay) > 100) { + $recalculate_state_user_transactions_balance = true; + $update_state_balance = true; + } + } + } + + if(!$recalculate_state_user_transactions_balance) { + $last_state_user_transaction = $state_user_transactions->last(); + if($last_state_user_transaction->balance <= 0) { + $recalculate_state_user_transactions_balance = true; + if(!$create_state_balance) { + $update_state_balance = true; + } + } + } + // second: do what is needed + if($clear_state_balance) { + $this->StateBalances->deleteAll(['state_user_id' => $stateUserId]); + } + + $transaction_ids = []; + if($recalculate_state_user_transactions_balance) { + $state_user_transactions_array = $state_user_transactions->toArray(); + foreach($state_user_transactions_array as $i => $state_user_transaction) { + $transaction_ids[$state_user_transaction->transaction_id] = $i; + } + + $transactions = $transactionsTable + ->find('all') + ->where(['id IN' => array_keys($transaction_ids)]) + ->contain(['TransactionCreations', 'TransactionSendCoins']); + + $balance_cursor = $this->StateBalances->newEntity(); + $i = 0; + foreach($transactions as $transaction) { + if($transaction->transaction_type_id > 2) { + continue; + } + $amount_date = null; + $amount = 0; + + if($transaction->transaction_type_id == 1) { + $temp = $transaction->transaction_creations[0]; + + $balance_temp = $this->StateBalances->newEntity(); + $balance_temp->amount = $temp->amount; + $balance_temp->record_date = $temp->target_date; + + $amount = $balance_temp->partDecay($transaction->received); + $amount_date = $transaction->received; + //$amount_date = + } else if($transaction->transaction_type_id == 2) { + $temp = $transaction->transaction_send_coins[0]; + $amount = intval($temp->amount); + // reverse if sender + if($stateUserId == $temp->state_user_id) { + $amount *= -1.0; + } + $amount_date = $transaction->received; + } + if($i == 0) { + $balance_cursor->amount = $amount; + } else { + $balance_cursor->amount = $balance_cursor->partDecay($amount_date) + $amount; + } + $balance_cursor->record_date = $amount_date; + $state_user_transaction_index = $transaction_ids[$transaction->id]; + $state_user_transactions_array[$state_user_transaction_index]->balance = $balance_cursor->amount; + $state_user_transactions_array[$state_user_transaction_index]->balance_date = $balance_cursor->record_date; + $i++; + } + $results = $stateUserTransactionsTable->saveMany($state_user_transactions_array); + $errors = []; + foreach($results as $i => $result) { + if(!$result) { + $errors[$i] = $state_user_transactions_array[$i]->getErrors(); + } + } + if(count($errors)) { + return ['success' => false, 'error' => 'error saving one ore more state user transactions', 'details' => $errors]; + } + } + $state_balance = null; + if($update_state_balance) { + $state_balance = $state_balances->first(); + } + else if($create_state_balance) { + $state_balance = $this->StateBalances->newEntity(); + $state_balance->state_user_id = $stateUserId; + } + if($state_balance) { + $state_balance->amount = $state_user_transactions->last()->balance; + $state_balance->record_date = $state_user_transactions->last()->balance_date; + if(!$this->StateBalances->save($state_balance)) { + return ['success' => false, 'error' => 'error saving state balance', 'details' => $state_balance->getErrors()]; + } + } + return true; + } public function overview() @@ -83,6 +198,10 @@ class StateBalancesController extends AppController return $result; } $user = $session->read('StateUser'); + $update_balance_result = $this->updateBalance($user['id']); + if($update_balance_result !== true) { + $this->addAdminError('StateBalances', 'overview', $update_balance_result, $user['id']); + } // sendRequestGDT // listPerEmailApi @@ -102,6 +221,7 @@ class StateBalancesController extends AppController //} // // + $stateUserTransactionsTable = TableRegistry::getTableLocator()->get('StateUserTransactions'); $creationsTable = TableRegistry::getTableLocator()->get('TransactionCreations'); $creationTransactions = $creationsTable @@ -170,17 +290,22 @@ class StateBalancesController extends AppController foreach ($transferTransactions as $sendCoins) { $type = ''; $otherUser = null; + $other_user_public = ''; if ($sendCoins->state_user_id == $user['id']) { $type = 'send'; if(isset($involvedUserIndices[$sendCoins->receiver_user_id])) { $otherUser = $involvedUserIndices[$sendCoins->receiver_user_id]; } + $other_user_public = bin2hex(stream_get_contents($sendCoins->receiver_public_key)); } else if ($sendCoins->receiver_user_id == $user['id']) { $type = 'receive'; if(isset($involvedUserIndices[$sendCoins->state_user_id])) { $otherUser = $involvedUserIndices[$sendCoins->state_user_id]; } + if($sendCoins->sender_public_key) { + $other_user_public = bin2hex(stream_get_contents($sendCoins->sender_public_key)); + } } if(null == $otherUser) { $otherUser = $this->StateBalances->StateUsers->newEntity(); @@ -192,17 +317,101 @@ class StateBalancesController extends AppController 'transaction_id' => $sendCoins->transaction_id, 'date' => $sendCoins->transaction->received, 'balance' => $sendCoins->amount, - 'memo' => $sendCoins->transaction->memo + 'memo' => $sendCoins->transaction->memo, + 'pubkey' => $other_user_public ]); } uasort($transactions, array($this, 'sortTransactions')); - $this->set('transactions', $transactions); + + // add decay transactions + $month_start_state_balance = null; + $current_state_balance = null; + $cursor = 0; + $transactions_reversed = array_reverse($transactions); + $maxI = count($transactions_reversed); + foreach($transactions_reversed as $i => $transaction) { + if(!isset($transaction['transaction_id'])) { + continue; + } + $transaction_id = $transaction['transaction_id']; + $decay_transaction = NULL; + $state_balance = $this->StateBalances->newEntity(); + + if($i > 0 && isset($transactions_reversed[$i-1]['transaction_id'])) { + $prev_transaction = $transactions_reversed[$i-1]; + $stateUserTransactions = $stateUserTransactionsTable + ->find() + ->where([ + 'transaction_id IN' => [$transaction_id, $prev_transaction['transaction_id']], + 'state_user_id' => $user['id'] + ]) + ->order(['transaction_id ASC']) + ->toArray(); + + $prev = $stateUserTransactions[0]; + if($prev->balance > 0) { + // var_dump($stateUserTransactions); + $current = $stateUserTransactions[1]; + $interval = $current->balance_date->diff($prev->balance_date); + $state_balance->amount = $prev->balance; + $state_balance->record_date = $prev->balance_date; + $diff_amount = $state_balance->partDecay($current->balance_date); + /* echo "prev date: $prev->balance_date, current date: $current->balance_date, interval: "; + var_dump($interval); + echo "
"; + echo "prev balance: $prev->balance
diff: $diff_amount
"; + echo "current balance: $current->balance
"; + * + */ + //echo $interval->format('%R%a days'); + $decay_transaction = [ + 'type' => 'decay', + 'balance' => -($prev->balance - $diff_amount), + 'decay_duration' => $interval->format('%a days, %H hours, %I minutes, %S seconds'), + 'memo' => '' + ]; + } + } + + if($decay_transaction) { + array_splice($transactions_reversed, $i + $cursor, 0, [$decay_transaction]); + $cursor++; + } + if($i == $maxI-1) { + $stateUserTransaction = $stateUserTransactionsTable + ->find() + ->where(['transaction_id' => $transaction_id, 'state_user_id' => $user['id']]) + ->order(['transaction_id ASC'])->first(); + //var_dump($stateUserTransaction); + $state_balance->amount = $stateUserTransaction->balance; + $state_balance->record_date = $stateUserTransaction->balance_date; + $transactions_reversed[] = [ + 'type' => 'decay', + 'balance' => -($stateUserTransaction->balance - $state_balance->decay), + 'decay_duration' => $stateUserTransaction->balance_date->timeAgoInWords(), + 'memo' => '' + ]; + + } + } + // for debugging + $calculated_balance = 0; + foreach($transactions_reversed as $tr) { + if($tr['type'] == 'send') { + $calculated_balance -= intval($tr['balance']); + } else { + $calculated_balance += intval($tr['balance']); + } + } + $this->set('calculated_balance', $calculated_balance); + + $this->set('transactions', array_reverse($transactions_reversed)); $this->set('transactionExecutingCount', $session->read('Transaction.executing')); $this->set('balance', $session->read('StateUser.balance')); $this->set('timeUsed', microtime(true) - $startTime); $this->set('gdtSum', $gdtSum); } - + public function ajaxGetBalance($session_id = 0) { if(!$session_id) { @@ -243,8 +452,9 @@ class StateBalancesController extends AppController return $this->returnJson(['state' => 'success', 'balance' => $state_balances[0]->amount]); } - - public function ajaxListTransactions($session_id = 0, $sort = 'ASC') + + + public function ajaxListTransactions($session_id, $page, $count) { if(!$session_id) { return $this->returnJson(['state' => 'error', 'msg' => 'invalid session id']); @@ -377,6 +587,42 @@ class StateBalancesController extends AppController ]); } + + + public function ajaxGdtOverview() + { + $gdtSum = 0; + $gdtCount = -1; + $session = $this->getRequest()->getSession(); + $user = $session->read('StateUser'); + + if(!$user) { + return $this->returnJson(['state' => 'error', 'msg' => 'user not found', 'details' => 'exist a valid session cookie?']); + } + $gdtEntries = $this->JsonRequestClient->sendRequestGDT(['email' => $user['email']], 'GdtEntries' . DS . 'sumPerEmailApi'); + + if('success' == $gdtEntries['state'] && 'success' == $gdtEntries['data']['state']) { + $gdtSum = intval($gdtEntries['data']['sum']); + if(isset($gdtEntries['data']['count'])) { + $gdtCount = intval($gdtEntries['data']['count']); + } + } else { + if($user) { + $this->addAdminError('StateBalancesController', 'ajaxGdtOverview', $gdtEntries, $user['id']); + } else { + $this->addAdminError('StateBalancesController', 'ajaxGdtOverview', $gdtEntries, 0); + } + } + + return $this->returnJson([ + 'state' => 'success', + 'transactions' => $transactions, + 'transactionExecutingCount' => $session->read('Transaction.executing'), + 'count' => $all_user_transactions_count + ]); + } + + public function overviewGdt() { $this->set( @@ -422,9 +668,9 @@ class StateBalancesController extends AppController //echo "gdtSum: $gdtSum
"; $this->set('gdtSum', $gdtSum); $this->set('ownEntries', $ownEntries); - $this->set('gdtSumPerEmail', $requestResult['data']['gdtSumPerEmail']); - $this->set('moreEntrysAsShown', $requestResult['data']['moreEntrysAsShown']); - $this->set('user', $user); + $this->set('gdtSumPerEmail', $requestResult['data']['gdtSumPerEmail']); + $this->set('moreEntrysAsShown', $requestResult['data']['moreEntrysAsShown']); + $this->set('user', $user); if (isset($requestResult['data']['publishers'])) { $publishers = $requestResult['data']['publishers']; @@ -435,6 +681,66 @@ class StateBalancesController extends AppController $this->Flash->error(__('Fehler beim GDT Server, bitte abwarten oder den Admin benachrichtigen!')); } } + + public function ajaxGdtTransactions() + { + $startTime = microtime(true); + $session = $this->getRequest()->getSession(); + $user = $session->read('StateUser'); + if(!$user) { + return $this->returnJson(['state' => 'error', 'msg' => 'user not found', 'details' => 'exist a valid session cookie?']); + } + + $requestResult = $this->JsonRequestClient->sendRequestGDT(['email' => $user['email']], 'GdtEntries' . DS . 'listPerEmailApi'); + $connectEntries = []; + $publishers = []; + + //var_dump($requestResult); + if('success' === $requestResult['state'] && 'success' === $requestResult['data']['state']) { + + //var_dump(array_keys($requestResult['data'])); + $ownEntries = $requestResult['data']['ownEntries']; + //$gdtEntries = $requestResult['data']['entries']; + + $gdtSum = 0; + foreach ($ownEntries as $i => $gdtEntry) { + $gdtSum += $gdtEntry['gdt']; + //echo "index: $i
"; + //var_dump($gdtEntry); + } + if (isset($requestResult['data']['connectEntrys'])) { + $connectEntries = $requestResult['data']['connectEntrys']; + + foreach ($connectEntries as $entry) { + //if(!$count) var_dump($entry); + //$count++; + $gdtSum += $entry['connect']['gdt_entry']['gdt']; + } + } + + //echo "gdtSum: $gdtSum
"; + + if (isset($requestResult['data']['publishers'])) { + $publishers = $requestResult['data']['publishers']; + } + } else { + $this->addAdminError('StateBalancesController', 'ajaxGdtTransactions', $requestResult, $user['id']); + //$this->Flash->error(__('Fehler beim GDT Server, bitte abwarten oder den Admin benachrichtigen!')); + return $this->returnJson(['state' => 'error', 'msg' => 'error from gdt server', 'details' => $requestResult]); + } + + + return $this->returnJson([ + 'state' => 'success', + 'gdtSum' => $gdtSum, + 'ownEntries' => $ownEntries, + 'connectEntries' => $connectEntries, + 'publishers' => $publishers, + 'gdtSumPerEmail' => $requestResult['data']['gdtSumPerEmail'], + 'moreEntrysAsShown' => $requestResult['data']['moreEntrysAsShown'], + 'timeUsed' => microtime(true) - $startTime + ]); + } public function sortTransactions($a, $b) { diff --git a/community_server/src/Controller/StateGroupAddressesController.php b/community_server/src/Controller/StateGroupAddressesController.php index 13f2b1f78..25afc82c7 100644 --- a/community_server/src/Controller/StateGroupAddressesController.php +++ b/community_server/src/Controller/StateGroupAddressesController.php @@ -20,7 +20,7 @@ class StateGroupAddressesController extends AppController public function index() { $this->paginate = [ - 'contain' => ['StateGroups', 'AddressTypes'] + 'contain' => ['AddressTypes'], ]; $stateGroupAddresses = $this->paginate($this->StateGroupAddresses); @@ -37,7 +37,7 @@ class StateGroupAddressesController extends AppController public function view($id = null) { $stateGroupAddress = $this->StateGroupAddresses->get($id, [ - 'contain' => ['StateGroups', 'AddressTypes'] + 'contain' => ['StateGroups', 'AddressTypes'], ]); $this->set('stateGroupAddress', $stateGroupAddress); @@ -75,7 +75,7 @@ class StateGroupAddressesController extends AppController public function edit($id = null) { $stateGroupAddress = $this->StateGroupAddresses->get($id, [ - 'contain' => [] + 'contain' => [], ]); if ($this->request->is(['patch', 'post', 'put'])) { $stateGroupAddress = $this->StateGroupAddresses->patchEntity($stateGroupAddress, $this->request->getData()); diff --git a/community_server/src/Controller/StateUserRolesController.php b/community_server/src/Controller/StateUserRolesController.php new file mode 100644 index 000000000..9238c7ad6 --- /dev/null +++ b/community_server/src/Controller/StateUserRolesController.php @@ -0,0 +1,185 @@ +loadComponent('JsonRequestClient'); + $this->Auth->allow([ +// 'search' + ]); + $this->set( + 'naviHierarchy', + (new NaviHierarchy())-> + add(new NaviHierarchyEntry(__('Startseite'), 'Dashboard', 'index', false))->add(new NaviHierarchyEntry(__('Benutzer suchen'), 'StateUsers', 'search', true)) + ); + } + + public function search() + { + $this->loadModel('StateUsers'); + $startTime = microtime(true); + I18n::setLocale('de_DE'); + $this->viewBuilder()->setLayout('frontend'); + + $searchForm = new UserSearchForm(); + + $timeUsed = microtime(true) - $startTime; + //$this->set('timeUsed', $timeUsed); + $this->set(compact('timeUsed', 'searchForm')); + + if ($this->request->is('post')) { + $requestData = $this->request->getData(); + + if ($searchForm->validate($requestData)) { + //var_dump($requestData); + $searchString = $requestData['search']; + + // find user on community server db + $globalSearch = '%' . $searchString . '%'; + $communityUsers = $this->StateUsers + ->find('all') + ->contain([]); + + $communityUsers->where(['OR' => [ + 'first_name LIKE' => $globalSearch, + 'last_name LIKE' => $globalSearch, + //'username LIKE' => $globalSearch, + 'email LIKE' => $globalSearch + ]]); + + //var_dump($communityUsers->toArray()); + $finalUserEntrys = []; + // detect states + $this->loadModel('Roles'); +// foreach ($pubkeySorted as $pubhex => $user) { + foreach($communityUsers as $communityUser) { + $finalUser = $communityUser; + $finalUser['pubkeyhex'] = bin2hex(stream_get_contents($communityUser->public_key)); + + $state_user_id = $communityUser->id; + $stateUserRole = $this->StateUserRoles->find('all')->where(['state_user_id' => $state_user_id])->all(); + + $role_ids = ""; + foreach ($stateUserRole as $userRole) { + if($role_ids != "") + $role_ids .= ",".$userRole->role_id; + else + $role_ids = $userRole->role_id; + } + + $roles = $this->Roles->find('all')->where(['id IN' => explode(",",$role_ids)])->all(); + + $role_names = ""; + foreach($roles as $role) + { + if($role_names != "") + $role_names .= "
".$role->title; + else + $role_names = $role->title; + } + + $finalUser['role_name'] = $role_names; + + array_push($finalUserEntrys, $finalUser); + } + + //var_dump($pubkeySorted); + } else { + $this->Flash->error(__('Something was invalid, please try again!')); + } + $this->set('finalUserEntrys', $finalUserEntrys); + } + $timeUsed = microtime(true) - $startTime; + $this->set('timeUsed', $timeUsed); + } + + public function assignRole() + { + $this->loadModel('Roles'); + $this->loadModel('StateUsers'); + + if ($this->request->is('post')) { + $requestData = $this->request->getData(); + + $public_hex = hex2bin($requestData['public_hex']); + + $stateUser = $this->StateUsers->find('all')->where(['public_key' => $public_hex])->first(); + + foreach($requestData['role_id'] as $role_id) + { + $newStateUserRole = $this->StateUserRoles->newEntity(); + + $post_data = []; + $post_data['state_user_id'] = $stateUser->id; + $post_data['role_id'] = $role_id; + $this->StateUserRoles->patchEntity($newStateUserRole, $post_data); + $this->StateUserRoles->save($newStateUserRole); + + } + + $this->Flash->success(__('Role has been assigned to User.')); + + return $this->redirect(['controller' => 'state-user-roles', 'action' => 'search']); + + } + + $assignRoleForm = new AssignRoleForm(); + + $public_hex = $this->request->getParam('pass')[0]; + + $publichex = hex2bin($public_hex); + + $stateUser = $this->StateUsers->find('all')->where(['public_key' => $publichex])->first(); + + $stateUserRoles = $this->StateUserRoles->find('all')->where(['state_user_id' => $stateUser->id])->all(); + + $role_ids = ""; + foreach ($stateUserRoles as $userRole) { + if($role_ids != "") + $role_ids .= ",".$userRole->role_id; + else + $role_ids = $userRole->role_id; + } + + $role_ids = explode(",", $role_ids); + + $roles = $this->Roles->find('list', array('fields' => array('id', 'title'))); + + + $this->set('roles', $roles); + $this->set('stateUser', $stateUser); + $this->set('role_ids', $role_ids); + $this->set('assignRoleForm', $assignRoleForm); + $this->set('public_hex', $public_hex); + } + +} diff --git a/community_server/src/Controller/StateUserTransactionsController.php b/community_server/src/Controller/StateUserTransactionsController.php index 8c252218c..e9008179e 100644 --- a/community_server/src/Controller/StateUserTransactionsController.php +++ b/community_server/src/Controller/StateUserTransactionsController.php @@ -20,6 +20,7 @@ class StateUserTransactionsController extends AppController $this->Auth->allow(['ajaxListTransactions']); //$this->loadComponent('JsonRequestClient'); } + /** * Index method * diff --git a/community_server/src/Controller/StateUsersController.php b/community_server/src/Controller/StateUsersController.php index d85f8b449..ddb3b695f 100644 --- a/community_server/src/Controller/StateUsersController.php +++ b/community_server/src/Controller/StateUsersController.php @@ -5,8 +5,8 @@ use Cake\Routing\Router; use Cake\I18n\I18n; use Cake\I18n\FrozenTime; use Cake\ORM\TableRegistry; -use Cake\Http\Client; use Cake\Core\Configure; +use Cake\Http\Client; use App\Controller\AppController; use App\Form\UserSearchForm; @@ -42,10 +42,11 @@ class StateUsersController extends AppController parent::initialize(); $this->loadComponent('GradidoNumber'); $this->loadComponent('JsonRequestClient'); + $this->loadComponent('Cookie'); $this->Auth->allow([ 'search', 'ajaxCopyLoginToCommunity', 'ajaxCopyCommunityToLogin', 'ajaxDelete', 'ajaxCountTransactions', 'ajaxVerificationEmailResend', - 'ajaxGetUserEmailVerificationCode', 'ajaxGetCSFRToken' + 'ajaxGetUserEmailVerificationCode', 'ajaxGetCSFRToken', 'login' ]); $this->set( 'naviHierarchy', @@ -72,11 +73,39 @@ class StateUsersController extends AppController public function listIdentHashes() { $stateUsers = $this->StateUsers->find('all')->toArray(); - foreach ($stateUsers as $i => $user) { - $stateUsers[$i]->identHash = TransactionCreation::DRMakeStringHash($user->email); + foreach($stateUsers as $i => $user) { + $stateUsers[$i]->identHash = TransactionCreation::DRMakeStringHash($user->email); } $this->set('stateUsers', $stateUsers); } + + public function login($session_id) + { + if(isset($session_id) && intval($session_id) !== 0) { + $loginServer = Configure::read('LoginServer'); + $http = new Client(); + + try { + $url = $loginServer['host'] . ':' . $loginServer['port']; + + $response = $http->get($url . '/login', ['session_id' => $session_id]); + $json = $response->getJson(); + + if (isset($json) && count($json) > 0) { + if ($json['state'] === 'success') { + $this->Cookie->configKey('GRADIDO_LOGIN', 'encryption', false); + $this->Cookie->write('GRADIDO_LOGIN', $session_id); + return $this->redirect(['controller' => 'Dashboard', 'action' => 'index']); + } + } + } catch (\Exception $e) { + $msg = $e->getMessage(); + $this->Flash->error(__('error http request: ') . $msg); + return $this->redirect(['controller' => 'Dashboard', 'action' => 'errorHttpRequest']); + } + } + throw new NotFoundException(); + } public function search() { @@ -85,12 +114,12 @@ class StateUsersController extends AppController $this->viewBuilder()->setLayout('frontend'); $session = $this->getRequest()->getSession(); $result = $this->requestLogin(); - if ($result !== true) { - return $result; + if($result !== true) { + return $result; } $user = $session->read('StateUser'); - if ($user['role'] != 'admin') { - return $this->redirect(['controller' => 'dashboard', 'action' => 'index']); + if($user['role'] != 'admin') { + return $this->redirect(['controller' => 'dashboard', 'action' => 'index']); } $searchForm = new UserSearchForm(); @@ -101,151 +130,149 @@ class StateUsersController extends AppController $this->set(compact('timeUsed', 'searchForm', 'csfr_token')); if ($this->request->is('post')) { - $requestData = $this->request->getData(); + $requestData = $this->request->getData(); - if ($searchForm->validate($requestData)) { - //var_dump($requestData); - $searchString = $requestData['search']; - $searchType = 'unknown'; - if (GenericValidation::email($searchString, [])) { - $searchType = 'email'; + if($searchForm->validate($requestData)) { + //var_dump($requestData); + $searchString = $requestData['search']; + $searchType = 'unknown'; + if(GenericValidation::email($searchString, [])) { + $searchType = 'email'; + } + // find users on login server + $resultJson = $this->JsonRequestClient->getUsers($session->read('session_id'), $searchString); + $loginServerUser = []; + if($resultJson['state'] == 'success') { + $dataJson = $resultJson['data']; + if($dataJson['state'] != 'success') { + if($dataJson['msg'] == 'session not found') { + $session->destroy(); + return $this->redirect(Router::url('/', true) . 'account', 303); + } + } + //var_dump($dataJson); + if(isset($dataJson['users'])) { + $loginServerUser = $dataJson['users']; + } + } + $pubkeySorted = []; + $emptyPubkeys = []; + foreach($loginServerUser as $u) { + if(!isset($u['public_hex']) || $u['public_hex'] == '') { + array_push($emptyPubkeys, $u); + } else { + if(!isset($pubkeySorted[$u['public_hex']])) { + $pubkeySorted[$u['public_hex']] = ['login' => [], 'community' => []]; } - // find users on login server - $resultJson = $this->JsonRequestClient->getUsers($session->read('session_id'), $searchString); - $loginServerUser = []; - if ($resultJson['state'] == 'success') { - $dataJson = $resultJson['data']; - if ($dataJson['state'] != 'success') { - if ($dataJson['msg'] == 'session not found') { - $session->destroy(); - return $this->redirect(Router::url('/', true) . 'account', 303); - } - } - //var_dump($dataJson); - if (isset($dataJson['users'])) { - $loginServerUser = $dataJson['users']; - } - } - $pubkeySorted = []; - $emptyPubkeys = []; - foreach ($loginServerUser as $u) { - if (!isset($u['public_hex']) || $u['public_hex'] == '') { - array_push($emptyPubkeys, $u); - } else { - if (!isset($pubkeySorted[$u['public_hex']])) { - $pubkeySorted[$u['public_hex']] = ['login' => [], 'community' => []]; - } - array_push($pubkeySorted[$u['public_hex']]['login'], $u); - } - } - // find user on community server db - $globalSearch = '%' . $searchString . '%'; - $communityUsers = $this->StateUsers + array_push($pubkeySorted[$u['public_hex']]['login'], $u); + } + } + // find user on community server db + $globalSearch = '%' . $searchString . '%'; + $communityUsers = $this->StateUsers ->find('all') ->contain(['StateBalances' => ['fields' => ['amount', 'state_user_id']]]); - $communityUsers->where(['OR' => [ + $communityUsers->where(['OR' => [ 'first_name LIKE' => $globalSearch, 'last_name LIKE' => $globalSearch, - //'username LIKE' => $globalSearch, 'email LIKE' => $globalSearch - ]]); + ]]); - //var_dump($communityUsers->toArray()); - foreach ($communityUsers as $u) { - $pubkey_hex = bin2hex(stream_get_contents($u->public_key)); - $u->public_hex = $pubkey_hex; - if (!isset($pubkeySorted[$pubkey_hex])) { - $pubkeySorted[$pubkey_hex] = ['login' => [], 'community' => []]; - } - array_push($pubkeySorted[$pubkey_hex]['community'], $u); + //var_dump($communityUsers->toArray()); + foreach($communityUsers as $u) { + $pubkey_hex = bin2hex(stream_get_contents($u->public_key)); + $u->public_hex = $pubkey_hex; + if(!isset($pubkeySorted[$pubkey_hex])) { + $pubkeySorted[$pubkey_hex] = ['login' => [], 'community' => []]; + } + array_push($pubkeySorted[$pubkey_hex]['community'], $u); + } + $finalUserEntrys = []; + // detect states + foreach($pubkeySorted as $pubhex => $user) { + $finalUser = []; + $state = 'account created'; + $color = 'secondary'; + $finalUser['balance'] = 0; + $finalUser['pubkeyhex'] = $pubhex; + $finalUser['created'] = null; + + if(count($user['community']) == 1) { + if(isset($user['community'][0]->state_balances) && + isset($user['community'][0]->state_balances[0]['amount'])) { + $finalUser['balance'] = $user['community'][0]->state_balances[0]->amount; } - $finalUserEntrys = []; - // detect states - foreach ($pubkeySorted as $pubhex => $user) { - $finalUser = []; - $state = 'account created'; - $color = 'secondary'; - $finalUser['balance'] = 0; - $finalUser['pubkeyhex'] = $pubhex; - $finalUser['created'] = null; + } - if (count($user['community']) == 1) { - if (isset($user['community'][0]->state_balances) && - isset($user['community'][0]->state_balances[0]['amount'])) { - $finalUser['balance'] = $user['community'][0]->state_balances[0]->amount; - } - } - - if (count($user['login']) == 0) { - $state = 'account not on login-server'; - $color = 'danger'; - if (count($user['community']) == 1) { - $c_user = $user['community'][0]; - $finalUser['name'] = $c_user->first_name . ' ' . $c_user->last_name; - $finalUser['first_name'] = $c_user->first_name; - $finalUser['last_name'] = $c_user->last_name; - //$finalUser['username'] = $c_user->username; - $finalUser['email'] = $c_user->email; - } - } elseif (count($user['login']) == 1) { - if ($user['login'][0]['email_checked'] == true) { - $state = 'email activated'; - $color = 'primary'; - - if (count($user['community']) == 1) { - $state = 'account copied to community'; - $color = 'success'; - //var_dump($user['community'][0]->state_balances[0]['amount']); - } - } else { - $state = 'email not activated'; - $color = 'warning'; - } - - $l_user = $user['login'][0]; - $finalUser['name'] = $l_user['first_name'] . ' ' . $l_user['last_name']; - $finalUser['first_name'] = $l_user['first_name']; - $finalUser['last_name'] = $l_user['last_name']; - //$finalUser['username'] = $l_user['username']; - $finalUser['email'] = $l_user['email']; - $finalUser['created'] = new FrozenTime($l_user['created']); - } else { - $state = 'account multiple times on login-server'; - $color = 'danger'; - } - $finalUser['indicator'] = ['name' => $state, 'color' => $color]; - array_push($finalUserEntrys, $finalUser); + if(count($user['login']) == 0) { + $state = 'account not on login-server'; + $color = 'danger'; + if(count($user['community']) == 1) { + $c_user = $user['community'][0]; + $finalUser['name'] = $c_user->first_name . ' ' . $c_user->last_name; + $finalUser['first_name'] = $c_user->first_name; + $finalUser['last_name'] = $c_user->last_name; + $finalUser['email'] = $c_user->email; } + } else if(count($user['login']) == 1) { + if($user['login'][0]['email_checked'] == true) { + $state = 'email activated'; + $color = 'primary'; + + if(count($user['community']) == 1) { + $state = 'account copied to community'; + $color = 'success'; + //var_dump($user['community'][0]->state_balances[0]['amount']); + } - foreach ($emptyPubkeys as $user) { - $finalUser = []; - $state = 'account not on community server'; - $color = 'secondary'; - if ($user['email_checked'] == false) { - $state = 'email not activated'; - $color = 'warning'; - } else { - $state = 'no keys'; - $color = 'warning'; - } - $finalUser['balance'] = 0; - $finalUser['pubkeyhex'] = ''; - $finalUser['name'] = $user['first_name'] . ' ' . $user['last_name']; - $finalUser['first_name'] = $user['first_name']; - $finalUser['last_name'] = $user['last_name']; - //$finalUser['username'] = $user['username']; - $finalUser['email'] = $user['email']; - $finalUser['created'] = new FrozenTime($user['created']); - $finalUser['indicator'] = ['name' => $state, 'color' => $color]; - array_push($finalUserEntrys, $finalUser); + } else { + $state = 'email not activated'; + $color = 'warning'; } - //var_dump($pubkeySorted); - } else { - $this->Flash->error(__('Something was invalid, please try again!')); + + $l_user = $user['login'][0]; + $finalUser['name'] = $l_user['first_name'] . ' ' . $l_user['last_name']; + $finalUser['first_name'] = $l_user['first_name']; + $finalUser['last_name'] = $l_user['last_name']; + $finalUser['email'] = $l_user['email']; + $finalUser['created'] = new FrozenTime($l_user['created']); + + } else { + $state = 'account multiple times on login-server'; + $color = 'danger'; + } + $finalUser['indicator'] = ['name' => $state, 'color' => $color]; + array_push($finalUserEntrys, $finalUser); } - $this->set('finalUserEntrys', $finalUserEntrys); + foreach($emptyPubkeys as $user) { + $finalUser = []; + $state = 'account not on community server'; + $color = 'secondary'; + if($user['email_checked'] == false) { + $state = 'email not activated'; + $color = 'warning'; + } else { + $state = 'no keys'; + $color = 'warning'; + } + $finalUser['balance'] = 0; + $finalUser['pubkeyhex'] = ''; + $finalUser['name'] = $user['first_name'] . ' ' . $user['last_name']; + $finalUser['first_name'] = $user['first_name']; + $finalUser['last_name'] = $user['last_name']; + $finalUser['email'] = $user['email']; + $finalUser['created'] = new FrozenTime($user['created']); + $finalUser['indicator'] = ['name' => $state, 'color' => $color]; + array_push($finalUserEntrys, $finalUser); + } + //var_dump($pubkeySorted); + } else { + $this->Flash->error(__('Something was invalid, please try again!')); + } + + $this->set('finalUserEntrys', $finalUserEntrys); } $timeUsed = microtime(true) - $startTime; $this->set('timeUsed', $timeUsed); @@ -253,187 +280,189 @@ class StateUsersController extends AppController public function ajaxCopyCommunityToLogin() { - $session = $this->getRequest()->getSession(); - $result = $this->requestLogin(); - if ($result !== true) { - return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']); - } - $user = $session->read('StateUser'); - if ($user['role'] != 'admin') { - return $this->returnJson(['state' => 'error', 'msg' => 'not an admin']); - } - if ($this->request->is('post')) { - $jsonData = $this->request->input('json_decode', true); - } - return $this->returnJson(['state' => 'error', 'msg' => 'no post request']); + $session = $this->getRequest()->getSession(); + $result = $this->requestLogin(); + if($result !== true) { + return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']); + } + $user = $session->read('StateUser'); + if($user['role'] != 'admin') { + return $this->returnJson(['state' => 'error', 'msg' => 'not an admin']); + } + if($this->request->is('post')) { + $jsonData = $this->request->input('json_decode', true); + } + return $this->returnJson(['state' => 'error', 'msg' => 'no post request']); } public function ajaxCopyLoginToCommunity() { - $session = $this->getRequest()->getSession(); - $result = $this->requestLogin(); - if ($result !== true) { - return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']); - } - $user = $session->read('StateUser'); - if ($user['role'] != 'admin') { - return $this->returnJson(['state' => 'error', 'msg' => 'not an admin']); - } + $session = $this->getRequest()->getSession(); + $result = $this->requestLogin(); + if($result !== true) { + return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']); + } + $user = $session->read('StateUser'); + if($user['role'] != 'admin') { + return $this->returnJson(['state' => 'error', 'msg' => 'not an admin']); + } - if ($this->request->is('post')) { - $jsonData = $this->request->input('json_decode', true); - //$user = $jsonData['user']; - //var_dump($jsonData); + if($this->request->is('post')) { + $jsonData = $this->request->input('json_decode', true); + //$user = $jsonData['user']; + //var_dump($jsonData); - $newStateUser = $this->StateUsers->newEntity(); - $this->StateUsers->patchEntity($newStateUser, $jsonData); - $newStateUser->public_key = hex2bin($jsonData['pubkeyhex']); + $newStateUser = $this->StateUsers->newEntity(); + $this->StateUsers->patchEntity($newStateUser, $jsonData); + $newStateUser->public_key = hex2bin($jsonData['pubkeyhex']); - if (!$this->StateUsers->save($newStateUser)) { - return $this->returnJson(['state' => 'error', 'msg' => 'error by saving', 'details' => json_encode($newStateUser->errors())]); - } + if(!$this->StateUsers->save($newStateUser)) { + return $this->returnJson(['state' => 'error', 'msg' => 'error by saving', 'details' => json_encode($newStateUser->errors())]); + } - return $this->returnJson(['state' => 'success']); - } - return $this->returnJson(['state' => 'error', 'msg' => 'no post request']); + return $this->returnJson(['state' => 'success']); + } + return $this->returnJson(['state' => 'error', 'msg' => 'no post request']); } public function ajaxVerificationEmailResend() { - $session = $this->getRequest()->getSession(); - $result = $this->requestLogin(); - if ($result !== true) { - return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']); - } - $user = $session->read('StateUser'); - if ($user['role'] != 'admin') { - return $this->returnJson(['state' => 'error', 'msg' => 'not an admin']); - } - if ($this->request->is('post')) { - $jsonData = $this->request->input('json_decode', true); - $email = $jsonData['email']; - $session_id = $session->read('session_id'); + $session = $this->getRequest()->getSession(); + $result = $this->requestLogin(); + if($result !== true) { + return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']); + } + $user = $session->read('StateUser'); + if($user['role'] != 'admin') { + return $this->returnJson(['state' => 'error', 'msg' => 'not an admin']); + } + if($this->request->is('post')) { + $jsonData = $this->request->input('json_decode', true); + $email = $jsonData['email']; + $session_id = $session->read('session_id'); - return $this->returnJson($this->JsonRequestClient->sendRequest(json_encode([ + return $this->returnJson($this->JsonRequestClient->sendRequest(json_encode([ 'session_id' => $session_id, 'email' => $email - ]), '/adminEmailVerificationResend')); - /*return $this->sendRequest(json_encode([ + ]), '/adminEmailVerificationResend')); + /*return $this->sendRequest(json_encode([ 'session_id' => $session_id, 'search' => $searchString - ]), '/getUsers');*/ - } - return $this->returnJson(['state' => 'error', 'msg' => 'no post request']); + ]), '/getUsers');*/ + } + return $this->returnJson(['state' => 'error', 'msg' => 'no post request']); } public function ajaxGetUserEmailVerificationCode() { - $session = $this->getRequest()->getSession(); - $result = $this->requestLogin(); - if ($result !== true) { - return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']); - } - $user = $session->read('StateUser'); - if ($user['role'] != 'admin') { - return $this->returnJson(['state' => 'error', 'msg' => 'not an admin']); - } - if ($this->request->is('post')) { - $jsonData = $this->request->input('json_decode', true); - $email = $jsonData['email']; - $session_id = $session->read('session_id'); + $session = $this->getRequest()->getSession(); + $result = $this->requestLogin(); + if($result !== true) { + return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']); + } + $user = $session->read('StateUser'); + if($user['role'] != 'admin') { + return $this->returnJson(['state' => 'error', 'msg' => 'not an admin']); + } + if($this->request->is('post')) { + $jsonData = $this->request->input('json_decode', true); + $email = $jsonData['email']; + $session_id = $session->read('session_id'); - return $this->returnJson($this->JsonRequestClient->sendRequest(json_encode([ + return $this->returnJson($this->JsonRequestClient->sendRequest(json_encode([ 'session_id' => $session_id, 'email' => $email, 'ask' => ['EmailVerificationCode.Register', 'loginServer.path'] - ]), '/getUserInfos')); - } - return $this->returnJson(['state' => 'error', 'msg' => 'no post request']); + ]), '/getUserInfos')); + } + return $this->returnJson(['state' => 'error', 'msg' => 'no post request']); } public function ajaxDelete() { - $session = $this->getRequest()->getSession(); - $result = $this->requestLogin(); - if ($result !== true) { - return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']); - } - $user = $session->read('StateUser'); - if ($user['role'] != 'admin') { - return $this->returnJson(['state' => 'error', 'msg' => 'not an admin']); - } + $session = $this->getRequest()->getSession(); + $result = $this->requestLogin(); + if($result !== true) { + return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']); + } + $user = $session->read('StateUser'); + if($user['role'] != 'admin') { + return $this->returnJson(['state' => 'error', 'msg' => 'not an admin']); + } - if ($this->request->is('post')) { - $jsonData = $this->request->input('json_decode', true); - //$user = $jsonData['user']; - //var_dump($jsonData); - $pubkey = hex2bin($jsonData['pubkeyhex']); - $stateUsers = $this->StateUsers->find('all')->where(['public_key' => $pubkey]); - if ($stateUsers->count() != 1) { - return $this->returnJson(['state' => 'error', 'msg' => 'invalid result count']); - } + if($this->request->is('post')) { + $jsonData = $this->request->input('json_decode', true); + //$user = $jsonData['user']; + //var_dump($jsonData); + $pubkey = hex2bin($jsonData['pubkeyhex']); + $stateUsers = $this->StateUsers->find('all')->where(['public_key' => $pubkey]); + if($stateUsers->count() != 1) { + return $this->returnJson(['state' => 'error', 'msg' => 'invalid result count']); + } - if ($this->StateUsers->delete($stateUsers->first())) { - return $this->returnJson(['state' => 'success']); - } else { - return $this->returnJson(['state' => 'error', 'msg' => 'error by deleting', 'details' => json_encode($stateUser->errors())]); - } - } - return $this->returnJson(['state' => 'error', 'msg' => 'no post request']); + if ($this->StateUsers->delete($stateUsers->first())) { + return $this->returnJson(['state' => 'success']); + } else { + return $this->returnJson(['state' => 'error', 'msg' => 'error by deleting', 'details' => json_encode($stateUser->errors())]); + } + } + return $this->returnJson(['state' => 'error', 'msg' => 'no post request']); } public function ajaxCountTransactions() { - $session = $this->getRequest()->getSession(); - $result = $this->requestLogin(); - if ($result !== true) { - return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']); - } - $user = $session->read('StateUser'); - if ($user['role'] != 'admin') { - return $this->returnJson(['state' => 'error', 'msg' => 'not an admin']); - } + $session = $this->getRequest()->getSession(); + $result = $this->requestLogin(); + if($result !== true) { + return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']); + } + $user = $session->read('StateUser'); + if($user['role'] != 'admin') { + return $this->returnJson(['state' => 'error', 'msg' => 'not an admin']); + } - if ($this->request->is('post')) { - $jsonData = $this->request->input('json_decode', true); - //$user = $jsonData['user']; - //var_dump($jsonData); - $pubkey = hex2bin($jsonData['pubkeyhex']); - $stateUsers = $this->StateUsers + if($this->request->is('post')) { + $jsonData = $this->request->input('json_decode', true); + //$user = $jsonData['user']; + //var_dump($jsonData); + $pubkey = hex2bin($jsonData['pubkeyhex']); + $stateUsers = $this->StateUsers ->find('all') ->where(['public_key' => $pubkey]) ->select(['id']); - if ($stateUsers->count() != 1) { - return $this->returnJson(['state' => 'error', 'msg' => 'invalid result count']); - } - $stateUser = $stateUsers->first(); - //var_dump($stateUser); - //var_dump($stateUser->toArray()); - $creationsTable = TableRegistry::getTableLocator()->get('TransactionCreations'); - $creationTransactions = $creationsTable + if($stateUsers->count() != 1) { + return $this->returnJson(['state' => 'error', 'msg' => 'invalid result count']); + } + $stateUser = $stateUsers->first(); + //var_dump($stateUser); + //var_dump($stateUser->toArray()); + $creationsTable = TableRegistry::getTableLocator()->get('TransactionCreations'); + $creationTransactions = $creationsTable ->find('all') ->select(['id']) ->where(['state_user_id' => $stateUser->id]); - $transferTable = TableRegistry::getTableLocator()->get('TransactionSendCoins'); - $transferTransactions = $transferTable + $transferTable = TableRegistry::getTableLocator()->get('TransactionSendCoins'); + $transferTransactions = $transferTable ->find('all') ->where(['OR' => ['state_user_id' => $stateUser->id, 'receiver_user_id' => $stateUser->id]]) ->select(['state_user_id', 'receiver_user_id']); - $counts = ['creation' => $creationTransactions->count(), 'receive' => 0, 'sended' => 0]; - foreach ($transferTransactions as $transfer) { - //var_dump($transfer); - if ($transfer->state_user_id == $stateUser->id) { - $counts['sended']++; - } - if ($transfer->receiver_user_id == $stateUser->id) { - $counts['receive']++; - } + $counts = ['creation' => $creationTransactions->count(), 'receive' => 0, 'sended' => 0]; + foreach($transferTransactions as $transfer) { + //var_dump($transfer); + if($transfer->state_user_id == $stateUser->id) { + $counts['sended']++; } - return $this->returnJson(['state' => 'success', 'counts' => $counts]); - } - return $this->returnJson(['state' => 'error', 'msg' => 'no post request']); + if($transfer->receiver_user_id == $stateUser->id) { + $counts['receive']++; + } + } + return $this->returnJson(['state' => 'success', 'counts' => $counts]); + + } + return $this->returnJson(['state' => 'error', 'msg' => 'no post request']); + } public function ajaxGetCSFRToken($session_id) @@ -541,9 +570,9 @@ class StateUsersController extends AppController } $this->Flash->error(__('The state user could not be saved. Please, try again.')); } - + $indices = $this->StateUsers->Indices->find('list', ['limit' => 200]); $stateGroups = $this->StateUsers->StateGroups->find('list', ['limit' => 200]); - $this->set(compact('stateUser', 'stateGroups')); + $this->set(compact('stateUser', 'indices', 'stateGroups')); } /** diff --git a/community_server/src/Controller/TransactionCreationsController.php b/community_server/src/Controller/TransactionCreationsController.php index 5e29a7997..5acfce2d7 100644 --- a/community_server/src/Controller/TransactionCreationsController.php +++ b/community_server/src/Controller/TransactionCreationsController.php @@ -335,6 +335,7 @@ class TransactionCreationsController extends AppController if (isset($requestData['add'])) { $mode = 'add'; } + //echo "mode: $mode
"; $memo = $requestData['memo']; $amountCent = $this->GradidoNumber->parseInputNumberToCentNumber($requestData['amount']); //$targetDate = $requestData['target_date']; @@ -342,17 +343,20 @@ class TransactionCreationsController extends AppController $this->Flash->error(__('No user selected')); } else { $users = $requestData['user']; + $pendingTransactionCount = $session->read('Transactions.pending'); + if ($pendingTransactionCount == null) { + $pendingTransactionCount = 0; + } if (isset($requestData['user_pending'])) { $pendings = $requestData['user_pending']; } else { $pendings = []; } - $receiverUsers = $stateUserTable - ->find('all') - ->where(['id IN' => array_keys($users)]) - ->select(['public_key', 'email', 'id']) - ->contain(false); - $transactions = []; + $receiverUsers = $stateUserTable->find('all') + ->where(['id IN' => array_keys($users)]) + ->select(['public_key', 'email', 'id']) + ->contain(false); + foreach ($receiverUsers as $receiverUser) { $localAmountCent = $amountCent; //$localTargetDate = $targetDate; @@ -369,25 +373,87 @@ class TransactionCreationsController extends AppController $pendings[$id] = $localAmountCent; } $pubKeyHex = bin2hex(stream_get_contents($receiverUser->public_key)); - $identHash = TransactionCreation::DRMakeStringHash($receiverUser->email); + $localTargetDateFrozen = FrozenDate::now(); $localTargetDateFrozen = $localTargetDateFrozen - ->year($localTargetDate['year']) - ->month($localTargetDate['month']) - ->day($localTargetDate['day']); - //echo "input: "; var_dump($localTargetDate);echo "
"; - //echo "output: "; var_dump($localTargetDateFrozen); - //die('a'); - $builderResult = TransactionCreation::build( - $localAmountCent, - $memo, - $pubKeyHex, - $identHash, - $localTargetDateFrozen - ); - if ($builderResult['state'] == 'success') { - array_push($transactions, base64_encode($builderResult['transactionBody']->serializeToString())); + ->year($localTargetDate['year']) + ->month($localTargetDate['month']) + ->day($localTargetDate['day']); + + $requestAnswear = $this->JsonRequestClient->sendRequest(json_encode([ + 'session_id' => $session->read('session_id'), + 'transaction_type' => 'creation', + 'memo' => $memo, + 'amount' => $localAmountCent, + 'target_pubkey' => $pubKeyHex, + 'target_date' => $localTargetDateFrozen + ]), '/createTransaction'); + + if('success' != $requestAnswear['state']) { + $this->addAdminError('TransactionCreations', 'createMulti', $requestAnswear, $user['id']); + if ($requestResult['type'] == 'request error') { + $this->Flash->error(__('Error by requesting LoginServer, please try again')); + } else { + $this->Flash->error(__('Error, please wait for the admin to fix it')); + } } + if('success' == $requestAnswear['state'] && 'success' == $requestAnswear['data']['state']) { + $pendingTransactionCount++; + //echo "pending transaction count: $pendingTransactionCount
"; + } else { + /* + * if request contain unknown parameter format, shouldn't happen't at all + * {"state": "error", "msg": "parameter format unknown"} + * if json parsing failed + * {"state": "error", "msg": "json exception", "details":"exception text"} + * if session_id is zero or not set + * {"state": "error", "msg": "session_id invalid"} + * if session id wasn't found on login server, if server was restartet or user logged out (also per timeout, default: 15 minutes) + * {"state": "error", "msg": "session not found"} + * if session hasn't active user, shouldn't happen't at all, login-server should be checked if happen + * {"state": "code error", "msg":"user is zero"} + * if transaction type not known + * {"state": "error", "msg":"transaction_type unknown"} + * if receiver wasn't known to Login-Server + * {"state": "not found", "msg":"receiver not found"} + * if receiver account disabled, and therefor cannto receive any coins + * {"state": "disabled", "msg":"receiver is disabled"} + * if amount is invalid in creation + * {"state": "invalid parameter", "msg":"invalid amount", "details":"GDD amount in GDD cent ]0,10000000]"} + * if transaction was okay and will be further proccessed + * {"state":"success"} + */ + $answear_data = $requestAnswear['data']; + if($answear_data['state'] === 'error') { + if($answear_data['msg'] === 'session_id invalid' || $answear_data['msg'] === 'session not found') { + $this->Flash->error(__('Fehler mit der Session, bitte logge dich erneut ein!')); + $this->set('timeUsed', microtime(true) - $startTime); + return; + } + if($answear_data['msg'] === 'user not in group') { + $this->Flash->error(__('Fehler, Benutzer gehört zu einer anderen Gruppe!')); + $this->set('timeUsed', microtime(true) - $startTime); + return; + } + } else if($answear_data['state'] === 'not found' && $answear_data['msg'] === 'receiver not found') { + $this->Flash->error(__('Der Empfänger wurde nicht auf dem Login-Server gefunden, hat er sein Konto schon angelegt?')); + $this->set('timeUsed', microtime(true) - $startTime); + return; + } else if($answear_data['state'] === 'disabled') { + $this->Flash->error(__('Der Empfänger ist deaktiviert, daher können ihm zurzeit keine Gradidos gesendet werden.')); + $this->set('timeUsed', microtime(true) - $startTime); + return; + } else if($answear_data['msg'] === 'invalid amount') { + $this->Flash->error(__('Der Betrag ist ungültig, er muss größer als 0 und <= 1000 sein.')); + $this->set('timeUsed', microtime(true) - $startTime); + return; + } else { + $this->Flash->error(__('Unbehandelter Fehler: ') . json_encode($answear_data)); + $this->set('timeUsed', microtime(true) - $startTime); + return; + } + } + } /*echo "pendings: "; var_dump($pendings); @@ -399,49 +465,17 @@ class TransactionCreationsController extends AppController } } $this->set('possibleReceivers', $possibleReceivers); - $creationTransactionCount = count($transactions); - if ($creationTransactionCount > 0) { + if ($pendingTransactionCount > 0) { $user_balance = 0; if (isset($user['balance'])) { $user_balance = $user['balance']; } - // $session_id, $base64Message, $user_balance = 0 - $requestResult = $this->JsonRequestClient->sendTransaction( - $session->read('session_id'), - $transactions, - $user_balance - ); - if ($requestResult['state'] != 'success') { - $this->addAdminError('TransactionCreations', 'createMulti', $requestResult, $user['id']); - if ($requestResult['type'] == 'request error') { - $this->Flash->error(__('Error by requesting LoginServer, please try again')); - } else { - $this->Flash->error(__('Error, please wait for the admin to fix it')); - } + $session->write('Transactions.pending', $pendingTransactionCount); + + if ($mode === 'next') { + return $this->redirect($this->loginServerUrl . 'account/checkTransactions', 303); } else { - $json = $requestResult['data']; - if ($json['state'] != 'success') { - if ($json['msg'] == 'session not found') { - $session->destroy(); - return $this->redirect($this->loginServerUrl . 'account', 303); - } else { - $this->addAdminError('TransactionCreations', 'createMulti', $json, $user['id']); - $this->Flash->error(__('Login Server Error, please wait for the admin to fix it')); - } - } else { - $pendingTransactionCount = $session->read('Transactions.pending'); - if ($pendingTransactionCount == null) { - $pendingTransactionCount = $creationTransactionCount; - } else { - $pendingTransactionCount += $creationTransactionCount; - } - $session->write('Transactions.pending', $pendingTransactionCount); - if ($mode === 'next') { - return $this->redirect($this->loginServerUrl . 'account/checkTransactions', 303); - } else { - $this->Flash->success(__('Transaction submitted for review.')); - } - } + $this->Flash->success(__('Transaction submitted for review.')); } } } diff --git a/community_server/src/Controller/TransactionSendCoinsController.php b/community_server/src/Controller/TransactionSendCoinsController.php index 2f48de063..12aa3e69a 100644 --- a/community_server/src/Controller/TransactionSendCoinsController.php +++ b/community_server/src/Controller/TransactionSendCoinsController.php @@ -57,8 +57,9 @@ class TransactionSendCoinsController extends AppController 'contain' => ['Transactions', 'StateUsers', 'ReceiverUsers'] ]; $transactionSendCoins = $this->paginate($this->TransactionSendCoins); + $simple = $this->TransactionSendCoins->find('all'); - $this->set(compact('transactionSendCoins')); + $this->set(compact('transactionSendCoins', 'simple')); } /** @@ -146,10 +147,17 @@ class TransactionSendCoinsController extends AppController } $user = $session->read('StateUser'); } + + $known_groups = $this->JsonRequestClient->sendRequest(json_encode([ + 'ask' => ['groups'] + ]), '/networkInfos'); + $transferForm = new TransferForm(); $this->set('transferForm', $transferForm); $this->set('timeUsed', microtime(true) - $startTime); + $this->set('groups', $known_groups['data']['data']['groups']); + $this->set('user', $user); if ($this->request->is('post')) { //$this->Flash->error(__('Wird zurzeit noch entwickelt!')); @@ -159,134 +167,93 @@ class TransactionSendCoinsController extends AppController if(isset($requestData['add'])) {$mode = 'add'; } if($transferForm->validate($requestData)) { - $receiverPubKeyHex = ''; - $senderPubKeyHex = $user['public_hex']; $amountCent = $this->GradidoNumber->parseInputNumberToCentNumber($requestData['amount']); if(!isset($user['balance']) || $amountCent > $user['balance']) { $this->Flash->error(__('Du hast nicht genug Geld!')); return; } - + $receiverEmail = $requestData['email']; if($receiverEmail === $user['email']) { $this->Flash->error(__('Du kannst dir leider nicht selbst Geld schicken!')); return; } - $receiverPubkeyHex =''; $requestAnswear = $this->JsonRequestClient->sendRequest(json_encode([ 'session_id' => $session->read('session_id'), - 'email' => $receiverEmail, - 'ask' => ['user.pubkeyhex', 'user.disabled'] - ]), '/getUserInfos'); + 'transaction_type' => 'transfer', + 'memo' => $requestData['memo'], + 'amount' => $amountCent, + 'target_group' => $known_groups['data']['data']['groups'][$requestData['group']], + 'target_email' => $receiverEmail + ]), '/createTransaction'); + if('success' == $requestAnswear['state'] && 'success' == $requestAnswear['data']['state']) { - // will be allways 64 byte long, even if it is empty - $receiverPubKeyHex = $requestAnswear['data']['userData']['pubkeyhex']; - } else { - $this->addAdminError('TransactionSendCoins', 'create', $requestAnswear, $user['id']); - $this->Flash->error(__('Der Empfänger wurde nicht auf dem Login-Server gefunden, hat er sein Konto schon angelegt?')); - $this->set('timeUsed', microtime(true) - $startTime); - return; - } - if($requestAnswear['data']['userData']['disabled']) { - $this->Flash->error(__('Der Empfänger ist deaktiviert, daher können ihm zurzeit keine Gradidos gesendet werden.')); - $this->set('timeUsed', microtime(true) - $startTime); - return; - } - - - if(0 == ord($receiverPubKeyHex)) { - $stateUserTable = TableRegistry::getTableLocator()->get('StateUsers'); - $receiverUser = $stateUserTable - ->find('all') - ->select(['public_key']) - ->contain(false) - ->where(['email' => $receiverEmail]); - - - if(!$receiverUser) { - $this->Flash->error(__('Diese E-Mail ist mir nicht bekannt, hat dein Empfänger denn schon ein Gradido-Konto?')); - $this->set('timeUsed', microtime(true) - $startTime); - return; - } - - if(isset($receiverUser->public_key)) { - $receiverPubKeyHex = bin2hex(stream_get_contents($receiverUser->public_key)); + $pendingTransactionCount = $session->read('Transactions.pending'); + if($pendingTransactionCount == null) { + $pendingTransactionCount = 1; } else { - $this->Flash->error(__('Das Konto mit der E-Mail: ' . $receiverEmail . ' wurde noch nicht aktiviert und kann noch keine GDD empfangen!')); + $pendingTransactionCount++; + } + $session->write('Transactions.pending', $pendingTransactionCount); + //echo "pending: " . $pendingTransactionCount; + if($mode === 'next') { + return $this->redirect($this->loginServerUrl . 'account/checkTransactions', 303); + } else { + $this->Flash->success(__('Transaction submitted for review.')); + } + } else { + + /* + * if request contain unknown parameter format, shouldn't happen't at all + * {"state": "error", "msg": "parameter format unknown"} + * if json parsing failed + * {"state": "error", "msg": "json exception", "details":"exception text"} + * if session_id is zero or not set + * {"state": "error", "msg": "session_id invalid"} + * if session id wasn't found on login server, if server was restartet or user logged out (also per timeout, default: 15 minutes) + * {"state": "error", "msg": "session not found"} + * if session hasn't active user, shouldn't happen't at all, login-server should be checked if happen + * {"state": "code error", "msg":"user is zero"} + * if transaction type not known + * {"state": "error", "msg":"transaction_type unknown"} + * if receiver wasn't known to Login-Server + * {"state": "not found", "msg":"receiver not found"} + * if receiver account disabled, and therefor cannto receive any coins + * {"state": "disabled", "msg":"receiver is disabled"} + * if transaction was okay and will be further proccessed + * {"state":"success"} + */ + $answear_data = $requestAnswear['data']; + if($answear_data['state'] === 'error') { + if($answear_data['msg'] === 'session_id invalid' || $answear_data['msg'] === 'session not found') { + $this->Flash->error(__('Fehler mit der Session, bitte logge dich erneut ein!')); + $this->set('timeUsed', microtime(true) - $startTime); + return; + } + if($answear_data['msg'] === 'user not in group') { + $this->Flash->error(__('Empfänger befindet sich nicht in Zielgruppe!')); + $this->set('timeUsed', microtime(true) - $startTime); + return; + } + } else if($answear_data['state'] === 'not found' && $answear_data['msg'] === 'receiver not found') { + $this->Flash->error(__('Der Empfänger wurde nicht auf dem Login-Server gefunden, hat er sein Konto schon angelegt?')); $this->set('timeUsed', microtime(true) - $startTime); return; - } + } else if($answear_data['state'] === 'disabled') { + $this->Flash->error(__('Der Empfänger ist deaktiviert, daher können ihm zurzeit keine Gradidos gesendet werden.')); + $this->set('timeUsed', microtime(true) - $startTime); + return; + } else { + $this->Flash->error(__('Unbehandelter Fehler: ') . json_encode($answear_data)); + $this->set('timeUsed', microtime(true) - $startTime); + return; + } } - //var_dump($sessionStateUser); - - $builderResult = TransactionTransfer::build( - $amountCent, - $requestData['memo'], - $receiverPubKeyHex, - $senderPubKeyHex - ); - if($builderResult['state'] === 'success') { - - $http = new Client(); - try { - $loginServer = Configure::read('LoginServer'); - $url = $loginServer['host'] . ':' . $loginServer['port']; - $session_id = $session->read('session_id'); - /* - * - * $response = $http->post( - * 'http://example.com/tasks', - * json_encode($data), - * ['type' => 'json'] - * ); - */ - $response = $http->post($url . '/checkTransaction', json_encode([ - 'session_id' => $session_id, - 'transaction_base64' => base64_encode($builderResult['transactionBody']->serializeToString()), - 'balance' => $user['balance'] - ]), ['type' => 'json']); - $json = $response->getJson(); - if($json['state'] != 'success') { - if($json['msg'] == 'session not found') { - $session->destroy(); - return $this->redirect($this->loginServerUrl . 'account', 303); - //$this->Flash->error(__('session not found, please login again')); - } else { - $this->Flash->error(__('login server return error: ' . json_encode($json))); - } - } else { - $pendingTransactionCount = $session->read('Transactions.pending'); - if($pendingTransactionCount == null) { - $pendingTransactionCount = 1; - } else { - $pendingTransactionCount++; - } - $session->write('Transactions.pending', $pendingTransactionCount); - //echo "pending: " . $pendingTransactionCount; - if($mode === 'next') { - return $this->redirect($this->loginServerUrl . 'account/checkTransactions', 303); - } else { - $this->Flash->success(__('Transaction submitted for review.')); - } - } - - } catch(\Exception $e) { - $msg = $e->getMessage(); - $this->Flash->error(__('error http request: ') . $msg); - } - - } else { - $this->Flash->error(__('No Valid Receiver Public given: ' . $receiverPubKeyHex)); - } - -// */ - } else { - $this->Flash->error(__('Something was invalid, please try again!')); } } - + $this->set('timeUsed', microtime(true) - $startTime); } diff --git a/community_server/src/Form/AssignRoleForm.php b/community_server/src/Form/AssignRoleForm.php new file mode 100644 index 000000000..158fa2296 --- /dev/null +++ b/community_server/src/Form/AssignRoleForm.php @@ -0,0 +1,30 @@ +addField('role_id', ['type' => 'string']); + } + + function validationDefault(Validator $validator) + { + $validator->setProvider('custom', 'App\Model\Validation\GenericValidation'); + + return $validator; + } + + + protected function _execute(array $data) + { + // Send an email. + return true; + } +} \ No newline at end of file diff --git a/community_server/src/Form/TransferForm.php b/community_server/src/Form/TransferForm.php index 5572ddaf9..cb135ab9e 100644 --- a/community_server/src/Form/TransferForm.php +++ b/community_server/src/Form/TransferForm.php @@ -1,5 +1,5 @@ -addField('email', ['type' => 'string']) + ->addField('group', ['type' => 'string']) ->addField('amount', ['type' => 'decimal', 'precision' => 2]) ->addField('memo', ['type' =>'text', 'default' => '', 'rows' => 3, 'maxlength' => 150]); } function validationDefault(Validator $validator) { - $validator->setProvider('custom', 'App\Model\Validation\TransactionValidation'); + $validator->setProvider('custom', 'App\Model\Validation\TransactionValidation'); /* $validator->add('receiver_pubkey_hex', 'length', [ 'rule' => ['length', 64], @@ -34,6 +35,12 @@ class TransferForm extends Form 'rule' => 'email', 'message' => __('A valid email address is required') ]) + ->add('group', 'custom', [ + 'rule' => 'alphaNumeric', + 'provider' => 'custom', + //'message' => __('Only Alpha Numeric Character allowed') + 'message' => __('No HTML Tags like > or < please.') + ]) ->add('memo', 'length', [ 'rule' => ['maxLength', 150], 'message' => __('The memo should contain max 150 character') @@ -54,7 +61,7 @@ class TransferForm extends Form ->allowEmptyString('receiver_pubkey_hex', null, 'create')*/ ->add('amount', 'custom', [ 'rule' => 'amount', - 'provider' => 'custom', + 'provider' => 'custom', 'message' => __('Please give a valid positive number with maximal 2 decimal places') ]); return $validator; @@ -72,4 +79,4 @@ class TransferForm extends Form // Send an email. return true; } -} +} \ No newline at end of file diff --git a/community_server/src/Model/Entity/AddressType.php b/community_server/src/Model/Entity/AddressType.php index 08dbba281..87d64d61a 100644 --- a/community_server/src/Model/Entity/AddressType.php +++ b/community_server/src/Model/Entity/AddressType.php @@ -8,7 +8,7 @@ use Cake\ORM\Entity; * * @property int $id * @property string $name - * @property string|null $text + * @property string $text * * @property \App\Model\Entity\StateGroupAddress[] $state_group_addresses * @property \App\Model\Entity\TransactionGroupAddaddres[] $transaction_group_addaddress @@ -28,6 +28,6 @@ class AddressType extends Entity 'name' => true, 'text' => true, 'state_group_addresses' => true, - 'transaction_group_addaddress' => true + 'transaction_group_addaddress' => true, ]; } diff --git a/community_server/src/Model/Entity/Role.php b/community_server/src/Model/Entity/Role.php new file mode 100644 index 000000000..ae0ddd0ba --- /dev/null +++ b/community_server/src/Model/Entity/Role.php @@ -0,0 +1,26 @@ + true, + ]; +} diff --git a/community_server/src/Model/Entity/StateBalance.php b/community_server/src/Model/Entity/StateBalance.php index 174e785b4..0381864b6 100644 --- a/community_server/src/Model/Entity/StateBalance.php +++ b/community_server/src/Model/Entity/StateBalance.php @@ -28,6 +28,7 @@ class StateBalance extends Entity protected $_accessible = [ 'state_user_id' => true, 'modified' => true, + 'record_date' => true, 'amount' => true, 'record_date' => true, 'state_user' => true @@ -35,6 +36,15 @@ class StateBalance extends Entity protected $_virtual = ['decay']; + private function convertToTimestamp($dateOrTime) + { + if(method_exists($dateOrTime, 'getTimestamp')) { + return $dateOrTime->getTimestamp(); + } else { + return $dateOrTime->i18nFormat(Time::UNIX_TIMESTAMP_FORMAT); + } + } + protected function _getDecay() { // decay factor in seconds per year @@ -44,7 +54,8 @@ class StateBalance extends Entity // SELECT TIMESTAMPDIFF(SECOND, modified, CURDATE()) AS age_in_seconds from state_balances // decay_for_duration = decay_factor^seconds // decay = gradido_cent * decay_for_duration - $decay_duration = intval(Time::now()->getTimestamp() - $this->record_date->getTimestamp()); + + $decay_duration = intval(Time::now()->getTimestamp() - $this->convertToTimestamp($this->record_date)); if($decay_duration === 0) { return $this->amount; } @@ -53,7 +64,7 @@ class StateBalance extends Entity } public function partDecay($target_date) { - $decay_duration = intval($target_date->getTimestamp() - $this->record_date->getTimestamp()); + $decay_duration = intval($this->convertToTimestamp($target_date) - $this->convertToTimestamp($this->record_date)); if($decay_duration <= 0) { return $this->amount; } @@ -62,6 +73,7 @@ class StateBalance extends Entity public function decayDuration($target_date) { - return intval($target_date->getTimestamp() - $this->record_date->getTimestamp()); + return intval($this->convertToTimestamp($target_date) - $this->convertToTimestamp($this->record_date)); } } + diff --git a/community_server/src/Model/Entity/StateGroupAddress.php b/community_server/src/Model/Entity/StateGroupAddress.php index e5ce71681..e2d669d09 100644 --- a/community_server/src/Model/Entity/StateGroupAddress.php +++ b/community_server/src/Model/Entity/StateGroupAddress.php @@ -7,7 +7,7 @@ use Cake\ORM\Entity; * StateGroupAddress Entity * * @property int $id - * @property int $state_group_id + * @property int $group_id * @property string|resource $public_key * @property int $address_type_id * @@ -26,10 +26,10 @@ class StateGroupAddress extends Entity * @var array */ protected $_accessible = [ - 'state_group_id' => true, + 'group_id' => true, 'public_key' => true, 'address_type_id' => true, 'state_group' => true, - 'address_type' => true + 'address_type' => true, ]; } diff --git a/community_server/src/Model/Entity/StateUserRole.php b/community_server/src/Model/Entity/StateUserRole.php new file mode 100644 index 000000000..d510bb025 --- /dev/null +++ b/community_server/src/Model/Entity/StateUserRole.php @@ -0,0 +1,30 @@ + true, + 'role_id' => true + ]; +} diff --git a/community_server/src/Model/Entity/TransactionGroupAddaddres.php b/community_server/src/Model/Entity/TransactionGroupAddaddres.php index 3cb73b9ee..30819209f 100644 --- a/community_server/src/Model/Entity/TransactionGroupAddaddres.php +++ b/community_server/src/Model/Entity/TransactionGroupAddaddres.php @@ -28,8 +28,10 @@ class TransactionGroupAddaddres extends Entity protected $_accessible = [ 'transaction_id' => true, 'address_type_id' => true, + 'remove_from_group' => true, 'public_key' => true, 'transaction' => true, - 'address_type' => true + 'address_type' => true, + 'state_user_id' => true ]; } diff --git a/community_server/src/Model/Messages/Proto/Gradido/CrossGroupTransfer.php b/community_server/src/Model/Messages/Proto/Gradido/CrossGroupTransfer.php new file mode 100644 index 000000000..781ac8f25 --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/CrossGroupTransfer.php @@ -0,0 +1,146 @@ +proto.gradido.CrossGroupTransfer + */ +class CrossGroupTransfer extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field .proto.gradido.TransferAmount sender = 1; + */ + private $sender = null; + /** + * Generated from protobuf field bytes receiver = 2; + */ + private $receiver = ''; + /** + * Generated from protobuf field string other_group = 3; + */ + private $other_group = ''; + /** + * this matches related OutboundTransfer.paired_transaction_id + * + * Generated from protobuf field .proto.gradido.Timestamp paired_transaction_id = 4; + */ + private $paired_transaction_id = null; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type \Proto\Gradido\TransferAmount $sender + * @type string $receiver + * @type string $other_group + * @type \Proto\Gradido\Timestamp $paired_transaction_id + * this matches related OutboundTransfer.paired_transaction_id + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\GradidoTransfer::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field .proto.gradido.TransferAmount sender = 1; + * @return \Proto\Gradido\TransferAmount + */ + public function getSender() + { + return $this->sender; + } + + /** + * Generated from protobuf field .proto.gradido.TransferAmount sender = 1; + * @param \Proto\Gradido\TransferAmount $var + * @return $this + */ + public function setSender($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\TransferAmount::class); + $this->sender = $var; + + return $this; + } + + /** + * Generated from protobuf field bytes receiver = 2; + * @return string + */ + public function getReceiver() + { + return $this->receiver; + } + + /** + * Generated from protobuf field bytes receiver = 2; + * @param string $var + * @return $this + */ + public function setReceiver($var) + { + GPBUtil::checkString($var, False); + $this->receiver = $var; + + return $this; + } + + /** + * Generated from protobuf field string other_group = 3; + * @return string + */ + public function getOtherGroup() + { + return $this->other_group; + } + + /** + * Generated from protobuf field string other_group = 3; + * @param string $var + * @return $this + */ + public function setOtherGroup($var) + { + GPBUtil::checkString($var, True); + $this->other_group = $var; + + return $this; + } + + /** + * this matches related OutboundTransfer.paired_transaction_id + * + * Generated from protobuf field .proto.gradido.Timestamp paired_transaction_id = 4; + * @return \Proto\Gradido\Timestamp + */ + public function getPairedTransactionId() + { + return $this->paired_transaction_id; + } + + /** + * this matches related OutboundTransfer.paired_transaction_id + * + * Generated from protobuf field .proto.gradido.Timestamp paired_transaction_id = 4; + * @param \Proto\Gradido\Timestamp $var + * @return $this + */ + public function setPairedTransactionId($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\Timestamp::class); + $this->paired_transaction_id = $var; + + return $this; + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/GradidoCreation.php b/community_server/src/Model/Messages/Proto/Gradido/GradidoCreation.php new file mode 100644 index 000000000..9b4c3208d --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/GradidoCreation.php @@ -0,0 +1,102 @@ +proto.gradido.GradidoCreation + */ +class GradidoCreation extends \Google\Protobuf\Internal\Message +{ + /** + * 40 Byte + * + * Generated from protobuf field .proto.gradido.TransferAmount receiver = 1; + */ + private $receiver = null; + /** + * 8 Byte + * + * Generated from protobuf field .proto.gradido.TimestampSeconds target_date = 3; + */ + private $target_date = null; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type \Proto\Gradido\TransferAmount $receiver + * 40 Byte + * @type \Proto\Gradido\TimestampSeconds $target_date + * 8 Byte + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\GradidoCreation::initOnce(); + parent::__construct($data); + } + + /** + * 40 Byte + * + * Generated from protobuf field .proto.gradido.TransferAmount receiver = 1; + * @return \Proto\Gradido\TransferAmount + */ + public function getReceiver() + { + return $this->receiver; + } + + /** + * 40 Byte + * + * Generated from protobuf field .proto.gradido.TransferAmount receiver = 1; + * @param \Proto\Gradido\TransferAmount $var + * @return $this + */ + public function setReceiver($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\TransferAmount::class); + $this->receiver = $var; + + return $this; + } + + /** + * 8 Byte + * + * Generated from protobuf field .proto.gradido.TimestampSeconds target_date = 3; + * @return \Proto\Gradido\TimestampSeconds + */ + public function getTargetDate() + { + return $this->target_date; + } + + /** + * 8 Byte + * + * Generated from protobuf field .proto.gradido.TimestampSeconds target_date = 3; + * @param \Proto\Gradido\TimestampSeconds $var + * @return $this + */ + public function setTargetDate($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\TimestampSeconds::class); + $this->target_date = $var; + + return $this; + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/GradidoTransaction.php b/community_server/src/Model/Messages/Proto/Gradido/GradidoTransaction.php new file mode 100644 index 000000000..b4b34c77e --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/GradidoTransaction.php @@ -0,0 +1,116 @@ +proto.gradido.GradidoTransaction + */ +class GradidoTransaction extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field .proto.gradido.SignatureMap sig_map = 1; + */ + private $sig_map = null; + /** + * TODO: consider if having concrete type wouldn't be better; + * having bytes would better show what is signed, still for + * blockchain it doesn't matter, as it has to be serialized again + * when validating + * UNICORN ANSWER: + * Maybe but hedera has at first concrete type and has depracted that now, so is must + * be reason for that + * + * Generated from protobuf field bytes body_bytes = 2; + */ + private $body_bytes = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type \Proto\Gradido\SignatureMap $sig_map + * @type string $body_bytes + * TODO: consider if having concrete type wouldn't be better; + * having bytes would better show what is signed, still for + * blockchain it doesn't matter, as it has to be serialized again + * when validating + * UNICORN ANSWER: + * Maybe but hedera has at first concrete type and has depracted that now, so is must + * be reason for that + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\GradidoTransaction::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field .proto.gradido.SignatureMap sig_map = 1; + * @return \Proto\Gradido\SignatureMap + */ + public function getSigMap() + { + return $this->sig_map; + } + + /** + * Generated from protobuf field .proto.gradido.SignatureMap sig_map = 1; + * @param \Proto\Gradido\SignatureMap $var + * @return $this + */ + public function setSigMap($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\SignatureMap::class); + $this->sig_map = $var; + + return $this; + } + + /** + * TODO: consider if having concrete type wouldn't be better; + * having bytes would better show what is signed, still for + * blockchain it doesn't matter, as it has to be serialized again + * when validating + * UNICORN ANSWER: + * Maybe but hedera has at first concrete type and has depracted that now, so is must + * be reason for that + * + * Generated from protobuf field bytes body_bytes = 2; + * @return string + */ + public function getBodyBytes() + { + return $this->body_bytes; + } + + /** + * TODO: consider if having concrete type wouldn't be better; + * having bytes would better show what is signed, still for + * blockchain it doesn't matter, as it has to be serialized again + * when validating + * UNICORN ANSWER: + * Maybe but hedera has at first concrete type and has depracted that now, so is must + * be reason for that + * + * Generated from protobuf field bytes body_bytes = 2; + * @param string $var + * @return $this + */ + public function setBodyBytes($var) + { + GPBUtil::checkString($var, False); + $this->body_bytes = $var; + + return $this; + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/GradidoTransfer.php b/community_server/src/Model/Messages/Proto/Gradido/GradidoTransfer.php new file mode 100644 index 000000000..a76954c2e --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/GradidoTransfer.php @@ -0,0 +1,109 @@ +proto.gradido.GradidoTransfer + */ +class GradidoTransfer extends \Google\Protobuf\Internal\Message +{ + protected $data; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type \Proto\Gradido\LocalTransfer $local + * @type \Proto\Gradido\CrossGroupTransfer $inbound + * @type \Proto\Gradido\CrossGroupTransfer $outbound + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\GradidoTransfer::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field .proto.gradido.LocalTransfer local = 1; + * @return \Proto\Gradido\LocalTransfer + */ + public function getLocal() + { + return $this->readOneof(1); + } + + /** + * Generated from protobuf field .proto.gradido.LocalTransfer local = 1; + * @param \Proto\Gradido\LocalTransfer $var + * @return $this + */ + public function setLocal($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\LocalTransfer::class); + $this->writeOneof(1, $var); + + return $this; + } + + /** + * Generated from protobuf field .proto.gradido.CrossGroupTransfer inbound = 2; + * @return \Proto\Gradido\CrossGroupTransfer + */ + public function getInbound() + { + return $this->readOneof(2); + } + + /** + * Generated from protobuf field .proto.gradido.CrossGroupTransfer inbound = 2; + * @param \Proto\Gradido\CrossGroupTransfer $var + * @return $this + */ + public function setInbound($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\CrossGroupTransfer::class); + $this->writeOneof(2, $var); + + return $this; + } + + /** + * Generated from protobuf field .proto.gradido.CrossGroupTransfer outbound = 3; + * @return \Proto\Gradido\CrossGroupTransfer + */ + public function getOutbound() + { + return $this->readOneof(3); + } + + /** + * Generated from protobuf field .proto.gradido.CrossGroupTransfer outbound = 3; + * @param \Proto\Gradido\CrossGroupTransfer $var + * @return $this + */ + public function setOutbound($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\CrossGroupTransfer::class); + $this->writeOneof(3, $var); + + return $this; + } + + /** + * @return string + */ + public function getData() + { + return $this->whichOneof("data"); + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/GroupFriendsUpdate.php b/community_server/src/Model/Messages/Proto/Gradido/GroupFriendsUpdate.php new file mode 100644 index 000000000..22e86d3e1 --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/GroupFriendsUpdate.php @@ -0,0 +1,92 @@ +proto.gradido.GroupFriendsUpdate + */ +class GroupFriendsUpdate extends \Google\Protobuf\Internal\Message +{ + /** + * char[16], alias + * + * Generated from protobuf field string group = 1; + */ + private $group = ''; + /** + * Generated from protobuf field .proto.gradido.GroupFriendsUpdate.Action action = 2; + */ + private $action = 0; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $group + * char[16], alias + * @type int $action + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\GroupFriendsUpdate::initOnce(); + parent::__construct($data); + } + + /** + * char[16], alias + * + * Generated from protobuf field string group = 1; + * @return string + */ + public function getGroup() + { + return $this->group; + } + + /** + * char[16], alias + * + * Generated from protobuf field string group = 1; + * @param string $var + * @return $this + */ + public function setGroup($var) + { + GPBUtil::checkString($var, True); + $this->group = $var; + + return $this; + } + + /** + * Generated from protobuf field .proto.gradido.GroupFriendsUpdate.Action action = 2; + * @return int + */ + public function getAction() + { + return $this->action; + } + + /** + * Generated from protobuf field .proto.gradido.GroupFriendsUpdate.Action action = 2; + * @param int $var + * @return $this + */ + public function setAction($var) + { + GPBUtil::checkEnum($var, \Proto\Gradido\GroupFriendsUpdate_Action::class); + $this->action = $var; + + return $this; + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/GroupFriendsUpdate/Action.php b/community_server/src/Model/Messages/Proto/Gradido/GroupFriendsUpdate/Action.php new file mode 100644 index 000000000..2a3396c55 --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/GroupFriendsUpdate/Action.php @@ -0,0 +1,51 @@ +proto.gradido.GroupFriendsUpdate.Action + */ +class Action +{ + /** + * Generated from protobuf enum ADD_FRIEND = 0; + */ + const ADD_FRIEND = 0; + /** + * Generated from protobuf enum REMOVE_FRIEND = 1; + */ + const REMOVE_FRIEND = 1; + + private static $valueToName = [ + self::ADD_FRIEND => 'ADD_FRIEND', + self::REMOVE_FRIEND => 'REMOVE_FRIEND', + ]; + + public static function name($value) + { + if (!isset(self::$valueToName[$value])) { + throw new UnexpectedValueException(sprintf( + 'Enum %s has no name defined for value %s', __CLASS__, $value)); + } + return self::$valueToName[$value]; + } + + + public static function value($name) + { + $const = __CLASS__ . '::' . strtoupper($name); + if (!defined($const)) { + throw new UnexpectedValueException(sprintf( + 'Enum %s has no value defined for name %s', __CLASS__, $name)); + } + return constant($const); + } +} + +// Adding a class alias for backwards compatibility with the previous class name. +class_alias(Action::class, \Proto\Gradido\GroupFriendsUpdate_Action::class); + diff --git a/community_server/src/Model/Messages/Proto/Gradido/GroupFriendsUpdate_Action.php b/community_server/src/Model/Messages/Proto/Gradido/GroupFriendsUpdate_Action.php new file mode 100644 index 000000000..24f8cef70 --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/GroupFriendsUpdate_Action.php @@ -0,0 +1,16 @@ +proto.gradido.GroupMemberUpdate + */ +class GroupMemberUpdate extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field bytes user_pubkey = 1; + */ + private $user_pubkey = ''; + /** + * Generated from protobuf field .proto.gradido.GroupMemberUpdate.MemberUpdateType member_update_type = 2; + */ + private $member_update_type = 0; + /** + * this only makes sense for user move, TODO: probably restructure + * + * Generated from protobuf field .proto.gradido.Timestamp paired_transaction_id = 3; + */ + private $paired_transaction_id = null; + /** + * Generated from protobuf field string target_group = 4; + */ + private $target_group = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $user_pubkey + * @type int $member_update_type + * @type \Proto\Gradido\Timestamp $paired_transaction_id + * this only makes sense for user move, TODO: probably restructure + * @type string $target_group + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\GroupMemberUpdate::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field bytes user_pubkey = 1; + * @return string + */ + public function getUserPubkey() + { + return $this->user_pubkey; + } + + /** + * Generated from protobuf field bytes user_pubkey = 1; + * @param string $var + * @return $this + */ + public function setUserPubkey($var) + { + GPBUtil::checkString($var, False); + $this->user_pubkey = $var; + + return $this; + } + + /** + * Generated from protobuf field .proto.gradido.GroupMemberUpdate.MemberUpdateType member_update_type = 2; + * @return int + */ + public function getMemberUpdateType() + { + return $this->member_update_type; + } + + /** + * Generated from protobuf field .proto.gradido.GroupMemberUpdate.MemberUpdateType member_update_type = 2; + * @param int $var + * @return $this + */ + public function setMemberUpdateType($var) + { + GPBUtil::checkEnum($var, \Proto\Gradido\GroupMemberUpdate_MemberUpdateType::class); + $this->member_update_type = $var; + + return $this; + } + + /** + * this only makes sense for user move, TODO: probably restructure + * + * Generated from protobuf field .proto.gradido.Timestamp paired_transaction_id = 3; + * @return \Proto\Gradido\Timestamp + */ + public function getPairedTransactionId() + { + return $this->paired_transaction_id; + } + + /** + * this only makes sense for user move, TODO: probably restructure + * + * Generated from protobuf field .proto.gradido.Timestamp paired_transaction_id = 3; + * @param \Proto\Gradido\Timestamp $var + * @return $this + */ + public function setPairedTransactionId($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\Timestamp::class); + $this->paired_transaction_id = $var; + + return $this; + } + + /** + * Generated from protobuf field string target_group = 4; + * @return string + */ + public function getTargetGroup() + { + return $this->target_group; + } + + /** + * Generated from protobuf field string target_group = 4; + * @param string $var + * @return $this + */ + public function setTargetGroup($var) + { + GPBUtil::checkString($var, True); + $this->target_group = $var; + + return $this; + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/GroupMemberUpdate/MemberUpdateType.php b/community_server/src/Model/Messages/Proto/Gradido/GroupMemberUpdate/MemberUpdateType.php new file mode 100644 index 000000000..57580cd75 --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/GroupMemberUpdate/MemberUpdateType.php @@ -0,0 +1,56 @@ +proto.gradido.GroupMemberUpdate.MemberUpdateType + */ +class MemberUpdateType +{ + /** + * Generated from protobuf enum ADD_USER = 0; + */ + const ADD_USER = 0; + /** + * Generated from protobuf enum MOVE_USER_INBOUND = 1; + */ + const MOVE_USER_INBOUND = 1; + /** + * Generated from protobuf enum MOVE_USER_OUTBOUND = 2; + */ + const MOVE_USER_OUTBOUND = 2; + + private static $valueToName = [ + self::ADD_USER => 'ADD_USER', + self::MOVE_USER_INBOUND => 'MOVE_USER_INBOUND', + self::MOVE_USER_OUTBOUND => 'MOVE_USER_OUTBOUND', + ]; + + public static function name($value) + { + if (!isset(self::$valueToName[$value])) { + throw new UnexpectedValueException(sprintf( + 'Enum %s has no name defined for value %s', __CLASS__, $value)); + } + return self::$valueToName[$value]; + } + + + public static function value($name) + { + $const = __CLASS__ . '::' . strtoupper($name); + if (!defined($const)) { + throw new UnexpectedValueException(sprintf( + 'Enum %s has no value defined for name %s', __CLASS__, $name)); + } + return constant($const); + } +} + +// Adding a class alias for backwards compatibility with the previous class name. +class_alias(MemberUpdateType::class, \Proto\Gradido\GroupMemberUpdate_MemberUpdateType::class); + diff --git a/community_server/src/Model/Messages/Proto/Gradido/GroupMemberUpdate_MemberUpdateType.php b/community_server/src/Model/Messages/Proto/Gradido/GroupMemberUpdate_MemberUpdateType.php new file mode 100644 index 000000000..b7080cf75 --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/GroupMemberUpdate_MemberUpdateType.php @@ -0,0 +1,16 @@ +proto.gradido.Key + */ +class Key extends \Google\Protobuf\Internal\Message +{ + protected $key; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $ed25519 + * ed25519 signature (libsodium default) + * @type string $ed25519_ref10 + * ed25519 ref10 signature + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\BasicTypes::initOnce(); + parent::__construct($data); + } + + /** + * ed25519 signature (libsodium default) + * + * Generated from protobuf field bytes ed25519 = 2; + * @return string + */ + public function getEd25519() + { + return $this->readOneof(2); + } + + /** + * ed25519 signature (libsodium default) + * + * Generated from protobuf field bytes ed25519 = 2; + * @param string $var + * @return $this + */ + public function setEd25519($var) + { + GPBUtil::checkString($var, False); + $this->writeOneof(2, $var); + + return $this; + } + + /** + * ed25519 ref10 signature + * + * Generated from protobuf field bytes ed25519_ref10 = 3; + * @return string + */ + public function getEd25519Ref10() + { + return $this->readOneof(3); + } + + /** + * ed25519 ref10 signature + * + * Generated from protobuf field bytes ed25519_ref10 = 3; + * @param string $var + * @return $this + */ + public function setEd25519Ref10($var) + { + GPBUtil::checkString($var, False); + $this->writeOneof(3, $var); + + return $this; + } + + /** + * @return string + */ + public function getKey() + { + return $this->whichOneof("key"); + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/LocalTransfer.php b/community_server/src/Model/Messages/Proto/Gradido/LocalTransfer.php new file mode 100644 index 000000000..58f5e23ae --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/LocalTransfer.php @@ -0,0 +1,85 @@ +proto.gradido.LocalTransfer + */ +class LocalTransfer extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field .proto.gradido.TransferAmount sender = 1; + */ + private $sender = null; + /** + * Generated from protobuf field bytes receiver = 2; + */ + private $receiver = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type \Proto\Gradido\TransferAmount $sender + * @type string $receiver + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\GradidoTransfer::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field .proto.gradido.TransferAmount sender = 1; + * @return \Proto\Gradido\TransferAmount + */ + public function getSender() + { + return $this->sender; + } + + /** + * Generated from protobuf field .proto.gradido.TransferAmount sender = 1; + * @param \Proto\Gradido\TransferAmount $var + * @return $this + */ + public function setSender($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\TransferAmount::class); + $this->sender = $var; + + return $this; + } + + /** + * Generated from protobuf field bytes receiver = 2; + * @return string + */ + public function getReceiver() + { + return $this->receiver; + } + + /** + * Generated from protobuf field bytes receiver = 2; + * @param string $var + * @return $this + */ + public function setReceiver($var) + { + GPBUtil::checkString($var, False); + $this->receiver = $var; + + return $this; + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/SignatureMap.php b/community_server/src/Model/Messages/Proto/Gradido/SignatureMap.php new file mode 100644 index 000000000..0977169d7 --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/SignatureMap.php @@ -0,0 +1,65 @@ +proto.gradido.SignatureMap + */ +class SignatureMap extends \Google\Protobuf\Internal\Message +{ + /** + * Each signature pair corresponds to a unique Key required to sign the transaction. + * + * Generated from protobuf field repeated .proto.gradido.SignaturePair sigPair = 1; + */ + private $sigPair; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type \Proto\Gradido\SignaturePair[]|\Google\Protobuf\Internal\RepeatedField $sigPair + * Each signature pair corresponds to a unique Key required to sign the transaction. + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\BasicTypes::initOnce(); + parent::__construct($data); + } + + /** + * Each signature pair corresponds to a unique Key required to sign the transaction. + * + * Generated from protobuf field repeated .proto.gradido.SignaturePair sigPair = 1; + * @return \Google\Protobuf\Internal\RepeatedField + */ + public function getSigPair() + { + return $this->sigPair; + } + + /** + * Each signature pair corresponds to a unique Key required to sign the transaction. + * + * Generated from protobuf field repeated .proto.gradido.SignaturePair sigPair = 1; + * @param \Proto\Gradido\SignaturePair[]|\Google\Protobuf\Internal\RepeatedField $var + * @return $this + */ + public function setSigPair($var) + { + $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Proto\Gradido\SignaturePair::class); + $this->sigPair = $arr; + + return $this; + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/SignaturePair.php b/community_server/src/Model/Messages/Proto/Gradido/SignaturePair.php new file mode 100644 index 000000000..2ef0e81c5 --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/SignaturePair.php @@ -0,0 +1,123 @@ +proto.gradido.SignaturePair + */ +class SignaturePair extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field bytes pubKey = 1; + */ + private $pubKey = ''; + protected $signature; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $pubKey + * @type string $ed25519 + * ed25519 signature (libsodium default) + * @type string $ed25519_ref10 + * ed25519 ref10 signature + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\BasicTypes::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field bytes pubKey = 1; + * @return string + */ + public function getPubKey() + { + return $this->pubKey; + } + + /** + * Generated from protobuf field bytes pubKey = 1; + * @param string $var + * @return $this + */ + public function setPubKey($var) + { + GPBUtil::checkString($var, False); + $this->pubKey = $var; + + return $this; + } + + /** + * ed25519 signature (libsodium default) + * + * Generated from protobuf field bytes ed25519 = 2; + * @return string + */ + public function getEd25519() + { + return $this->readOneof(2); + } + + /** + * ed25519 signature (libsodium default) + * + * Generated from protobuf field bytes ed25519 = 2; + * @param string $var + * @return $this + */ + public function setEd25519($var) + { + GPBUtil::checkString($var, False); + $this->writeOneof(2, $var); + + return $this; + } + + /** + * ed25519 ref10 signature + * + * Generated from protobuf field bytes ed25519_ref10 = 3; + * @return string + */ + public function getEd25519Ref10() + { + return $this->readOneof(3); + } + + /** + * ed25519 ref10 signature + * + * Generated from protobuf field bytes ed25519_ref10 = 3; + * @param string $var + * @return $this + */ + public function setEd25519Ref10($var) + { + GPBUtil::checkString($var, False); + $this->writeOneof(3, $var); + + return $this; + } + + /** + * @return string + */ + public function getSignature() + { + return $this->whichOneof("signature"); + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/Timestamp.php b/community_server/src/Model/Messages/Proto/Gradido/Timestamp.php new file mode 100644 index 000000000..ae3ecea0e --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/Timestamp.php @@ -0,0 +1,101 @@ +proto.gradido.Timestamp + */ +class Timestamp extends \Google\Protobuf\Internal\Message +{ + /** + * Number of complete seconds since the start of the epoch + * + * Generated from protobuf field int64 seconds = 1; + */ + private $seconds = 0; + /** + * Number of nanoseconds since the start of the last second + * + * Generated from protobuf field int32 nanos = 2; + */ + private $nanos = 0; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type int|string $seconds + * Number of complete seconds since the start of the epoch + * @type int $nanos + * Number of nanoseconds since the start of the last second + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\BasicTypes::initOnce(); + parent::__construct($data); + } + + /** + * Number of complete seconds since the start of the epoch + * + * Generated from protobuf field int64 seconds = 1; + * @return int|string + */ + public function getSeconds() + { + return $this->seconds; + } + + /** + * Number of complete seconds since the start of the epoch + * + * Generated from protobuf field int64 seconds = 1; + * @param int|string $var + * @return $this + */ + public function setSeconds($var) + { + GPBUtil::checkInt64($var); + $this->seconds = $var; + + return $this; + } + + /** + * Number of nanoseconds since the start of the last second + * + * Generated from protobuf field int32 nanos = 2; + * @return int + */ + public function getNanos() + { + return $this->nanos; + } + + /** + * Number of nanoseconds since the start of the last second + * + * Generated from protobuf field int32 nanos = 2; + * @param int $var + * @return $this + */ + public function setNanos($var) + { + GPBUtil::checkInt32($var); + $this->nanos = $var; + + return $this; + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/TimestampSeconds.php b/community_server/src/Model/Messages/Proto/Gradido/TimestampSeconds.php new file mode 100644 index 000000000..d31aa40d2 --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/TimestampSeconds.php @@ -0,0 +1,67 @@ +proto.gradido.TimestampSeconds + */ +class TimestampSeconds extends \Google\Protobuf\Internal\Message +{ + /** + * Number of complete seconds since the start of the epoch + * + * Generated from protobuf field int64 seconds = 1; + */ + private $seconds = 0; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type int|string $seconds + * Number of complete seconds since the start of the epoch + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\BasicTypes::initOnce(); + parent::__construct($data); + } + + /** + * Number of complete seconds since the start of the epoch + * + * Generated from protobuf field int64 seconds = 1; + * @return int|string + */ + public function getSeconds() + { + return $this->seconds; + } + + /** + * Number of complete seconds since the start of the epoch + * + * Generated from protobuf field int64 seconds = 1; + * @param int|string $var + * @return $this + */ + public function setSeconds($var) + { + GPBUtil::checkInt64($var); + $this->seconds = $var; + + return $this; + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/TransactionBody.php b/community_server/src/Model/Messages/Proto/Gradido/TransactionBody.php new file mode 100644 index 000000000..0484ecc92 --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/TransactionBody.php @@ -0,0 +1,193 @@ +proto.gradido.TransactionBody + */ +class TransactionBody extends \Google\Protobuf\Internal\Message +{ + /** + * max 150 chars + * + * Generated from protobuf field string memo = 1; + */ + private $memo = ''; + /** + * Generated from protobuf field uint64 version_number = 6; + */ + private $version_number = 0; + protected $data; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $memo + * max 150 chars + * @type \Proto\Gradido\GradidoTransfer $transfer + * @type \Proto\Gradido\GradidoCreation $creation + * @type \Proto\Gradido\GroupFriendsUpdate $group_friends_update + * @type \Proto\Gradido\GroupMemberUpdate $group_member_update + * @type int|string $version_number + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\TransactionBody::initOnce(); + parent::__construct($data); + } + + /** + * max 150 chars + * + * Generated from protobuf field string memo = 1; + * @return string + */ + public function getMemo() + { + return $this->memo; + } + + /** + * max 150 chars + * + * Generated from protobuf field string memo = 1; + * @param string $var + * @return $this + */ + public function setMemo($var) + { + GPBUtil::checkString($var, True); + $this->memo = $var; + + return $this; + } + + /** + * Generated from protobuf field .proto.gradido.GradidoTransfer transfer = 2; + * @return \Proto\Gradido\GradidoTransfer + */ + public function getTransfer() + { + return $this->readOneof(2); + } + + /** + * Generated from protobuf field .proto.gradido.GradidoTransfer transfer = 2; + * @param \Proto\Gradido\GradidoTransfer $var + * @return $this + */ + public function setTransfer($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\GradidoTransfer::class); + $this->writeOneof(2, $var); + + return $this; + } + + /** + * Generated from protobuf field .proto.gradido.GradidoCreation creation = 3; + * @return \Proto\Gradido\GradidoCreation + */ + public function getCreation() + { + return $this->readOneof(3); + } + + /** + * Generated from protobuf field .proto.gradido.GradidoCreation creation = 3; + * @param \Proto\Gradido\GradidoCreation $var + * @return $this + */ + public function setCreation($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\GradidoCreation::class); + $this->writeOneof(3, $var); + + return $this; + } + + /** + * Generated from protobuf field .proto.gradido.GroupFriendsUpdate group_friends_update = 4; + * @return \Proto\Gradido\GroupFriendsUpdate + */ + public function getGroupFriendsUpdate() + { + return $this->readOneof(4); + } + + /** + * Generated from protobuf field .proto.gradido.GroupFriendsUpdate group_friends_update = 4; + * @param \Proto\Gradido\GroupFriendsUpdate $var + * @return $this + */ + public function setGroupFriendsUpdate($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\GroupFriendsUpdate::class); + $this->writeOneof(4, $var); + + return $this; + } + + /** + * Generated from protobuf field .proto.gradido.GroupMemberUpdate group_member_update = 5; + * @return \Proto\Gradido\GroupMemberUpdate + */ + public function getGroupMemberUpdate() + { + return $this->readOneof(5); + } + + /** + * Generated from protobuf field .proto.gradido.GroupMemberUpdate group_member_update = 5; + * @param \Proto\Gradido\GroupMemberUpdate $var + * @return $this + */ + public function setGroupMemberUpdate($var) + { + GPBUtil::checkMessage($var, \Proto\Gradido\GroupMemberUpdate::class); + $this->writeOneof(5, $var); + + return $this; + } + + /** + * Generated from protobuf field uint64 version_number = 6; + * @return int|string + */ + public function getVersionNumber() + { + return $this->version_number; + } + + /** + * Generated from protobuf field uint64 version_number = 6; + * @param int|string $var + * @return $this + */ + public function setVersionNumber($var) + { + GPBUtil::checkUint64($var); + $this->version_number = $var; + + return $this; + } + + /** + * @return string + */ + public function getData() + { + return $this->whichOneof("data"); + } + +} + diff --git a/community_server/src/Model/Messages/Proto/Gradido/TransferAmount.php b/community_server/src/Model/Messages/Proto/Gradido/TransferAmount.php new file mode 100644 index 000000000..7673582fa --- /dev/null +++ b/community_server/src/Model/Messages/Proto/Gradido/TransferAmount.php @@ -0,0 +1,85 @@ +proto.gradido.TransferAmount + */ +class TransferAmount extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field bytes pubkey = 1; + */ + private $pubkey = ''; + /** + * Generated from protobuf field sint64 amount = 2; + */ + private $amount = 0; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $pubkey + * @type int|string $amount + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Gradido\BasicTypes::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field bytes pubkey = 1; + * @return string + */ + public function getPubkey() + { + return $this->pubkey; + } + + /** + * Generated from protobuf field bytes pubkey = 1; + * @param string $var + * @return $this + */ + public function setPubkey($var) + { + GPBUtil::checkString($var, False); + $this->pubkey = $var; + + return $this; + } + + /** + * Generated from protobuf field sint64 amount = 2; + * @return int|string + */ + public function getAmount() + { + return $this->amount; + } + + /** + * Generated from protobuf field sint64 amount = 2; + * @param int|string $var + * @return $this + */ + public function setAmount($var) + { + GPBUtil::checkInt64($var); + $this->amount = $var; + + return $this; + } + +} + diff --git a/community_server/src/Model/Table/AddressTypesTable.php b/community_server/src/Model/Table/AddressTypesTable.php index ea0c86d6e..ba94c99c7 100644 --- a/community_server/src/Model/Table/AddressTypesTable.php +++ b/community_server/src/Model/Table/AddressTypesTable.php @@ -38,10 +38,10 @@ class AddressTypesTable extends Table $this->setPrimaryKey('id'); $this->hasMany('StateGroupAddresses', [ - 'foreignKey' => 'address_type_id' + 'foreignKey' => 'address_type_id', ]); $this->hasMany('TransactionGroupAddaddress', [ - 'foreignKey' => 'address_type_id' + 'foreignKey' => 'address_type_id', ]); } @@ -54,19 +54,20 @@ class AddressTypesTable extends Table public function validationDefault(Validator $validator) { $validator - ->integer('id') + ->nonNegativeInteger('id') ->allowEmptyString('id', null, 'create'); $validator ->scalar('name') - ->maxLength('name', 25) + ->maxLength('name', 45) ->requirePresence('name', 'create') ->notEmptyString('name'); $validator ->scalar('text') ->maxLength('text', 255) - ->allowEmptyString('text'); + ->requirePresence('text', 'create') + ->notEmptyString('text'); return $validator; } diff --git a/community_server/src/Model/Table/RolesTable.php b/community_server/src/Model/Table/RolesTable.php new file mode 100644 index 000000000..940b7f246 --- /dev/null +++ b/community_server/src/Model/Table/RolesTable.php @@ -0,0 +1,72 @@ +setTable('roles'); + $this->setDisplayField('title'); + $this->setPrimaryKey('id'); + } + + /** + * Default validation rules. + * + * @param \Cake\Validation\Validator $validator Validator instance. + * @return \Cake\Validation\Validator + */ + public function validationDefault(Validator $validator) + { + $validator + ->integer('id') + ->allowEmptyString('id', null, 'create') + ->add('id', 'unique', ['rule' => 'validateUnique', 'provider' => 'table']); + + $validator + ->scalar('title') + ->maxLength('title', 255) + ->allowEmptyString('title'); + + return $validator; + } + + /** + * Returns a rules checker object that will be used for validating + * application integrity. + * + * @param \Cake\ORM\RulesChecker $rules The rules object to be modified. + * @return \Cake\ORM\RulesChecker + */ + public function buildRules(RulesChecker $rules) + { + $rules->add($rules->isUnique(['id'])); + + return $rules; + } +} diff --git a/community_server/src/Model/Table/StateBalancesTable.php b/community_server/src/Model/Table/StateBalancesTable.php index 85b2f7837..cae6e0fe8 100644 --- a/community_server/src/Model/Table/StateBalancesTable.php +++ b/community_server/src/Model/Table/StateBalancesTable.php @@ -6,6 +6,10 @@ use Cake\ORM\RulesChecker; use Cake\ORM\Table; use Cake\Validation\Validator; +use Cake\ORM\TableRegistry; +use Cake\I18n\Date; +use Cake\I18n\Time; + /** * StateBalances Model * @@ -78,4 +82,271 @@ class StateBalancesTable extends Table return $rules; } + /* + * create new state balance at beginning of next month from $previousStateBalance + * calculate decay for the time diff + */ + private function calculateStateBalance($previousStateBalance) + { + $entity = $this->newEntity(); + $entity->state_user_id = $previousStateBalance->state_user_id; + $newDate = $previousStateBalance->record_date; + $newDate->day(1); + if($newDate->month <= 12) { + $newDate->month($newDate->month + 1); + } else { + $newDate->month(1); + $newDate->year($newDate->year + 1); + } + $entity->record_date = $newDate; + $entity->amount = $previousStateBalance->partDecay($newDate); + if($this->save($entity)) { + return $entity; + } + return ['state' => 'error', 'msg' => 'couldn\'t save', 'details' => $entity->getErrors()]; + } + + public function sortTransactions($a, $b) + { + if ($a['date'] == $b['date']) { + return 0; + } + return ($a['date'] > $b['date']) ? -1 : 1; + } + /* + * calculate balance at end of month + * work only if state balance at begin of month exist + * use transaction_send_coins and transaction_creations + */ + public function updateLastStateBalanceOfMonth($month, $year, $state_user_id) + { + $first_of_month = new Time("$year-$month-01 00:00"); + $last_of_month = new Time($first_of_month); + $last_of_month->addMonth(1); + $last_of_month->subSecond(1); + $query = $this->find('all') + ->where(['AND' => [ + 'state_user_id' => $state_user_id, + 'record_date >=' => $first_of_month, + 'record_date <=' => $last_of_month + ]]) + ->order(['record_date' => 'ASC']); + if($query->isEmpty()) { + return [ + 'state' => 'error', + 'msg' => 'no state balance in this month found', + 'details' => [ + 'month' => $month, + 'year' => $year, + 'state_user_id' => $state_user_id + ] + ]; + } + // get transactions from this month + $balance_changes = []; + $transactionCreationsTable = TableRegistry::getTableLocator()->get('TransactionCreations'); + $transactionTransfersTable = TableRegistry::getTableLocator()->get('TransactionSendCoins'); + $relevant_creations = $transactionCreationsTable + ->find('all') + ->where(['AND' => [ + 'state_user_id' => $state_user_id, + 'target_date >=' => $first_of_month, + 'target_date <=' => $last_of_month + ]])->contain(false); + foreach($relevant_creations as $creation) { + $balance_changes[] = ['amount' => $creation->amount, 'date' => $creation->target_date]; + } + $relevant_transfers = $transactionTransfersTable + ->find('all') + ->where(['AND' => [ + 'OR' => [ + 'state_user_id' => $state_user_id, + 'receiver_user_id' => $state_user_id + ], + 'transaction.received >= ' => $first_of_month, + 'transaction.received <=' => $last_of_month + ]])->contain(['Transactions']); + debug($relevant_transfers); + foreach($relevant_transfers as $transfer) { + $amount = $transfer->amount; + // if it is a send transaction, negate the value + if($transfer->state_user_id == $state_user_id) { + $amount *= -1.0; + } + $balance_changes[] = ['amount' => $amount, 'date' => $transfer->transaction->received]; + } + uasort($balance_changes, array($this, 'sortTransactions')); + $current_state_balance = null; + if($query->count() == 1) { + $current_state_balance = $this->newEntity(); + $current_state_balance->amount = $query->first()->amount; + $current_state_balance->state_user_id = $state_user_id; + $current_state_balance->record_date = $query->first()->record_date; + } else if($query->count() == 2) { + $array = $query->toArray(); + $current_state_balance = $array[1]; + } else { + throw new Exception('Should\'n occure, never'); + } + + foreach($balance_changes as $change) { + $current_state_balance->amount = $current_state_balance->getDecay($change['date']); + $current_state_balance->amount += $change['amount']; + $current_state_balance->record_date = $change['date']; + } + if(!$this->save($current_state_balance)) { + return ['state' => 'error', 'msg' => 'couldn\'t save', 'details' => $current_state_balance->getErrors()]; + } + return $current_state_balance; + } + + /* + * getting start balance for month + * create and create all missing state_balances before if not exist + * in while loop + */ + + public function chooseForMonthAndUser($month, $year, $state_user_id) + { + $first_of_month = new Time("$year-$month-01 00:00"); + $last_of_month = new Time($first_of_month); + $last_of_month->addMonth(1); + $last_of_month->subSecond(1); + //echo "first of month: " . $first_of_month->i18nFormat() . ", last of month: " . $last_of_month->i18nFormat() . "
"; + $query = $this->find('all'); + + $query->select([ + 'month' => $query->func()->month(['record_date' => 'identifier']), + 'year' => $query->func()->year(['record_date' => 'identifier']) + ])->select($this) + //->where(['month' => $month, 'year' => $year, 'state_user_id' => $state_user_id]) + ->where(['AND' => [ + 'state_user_id' => $state_user_id, + 'record_date >=' => $first_of_month, + 'record_date <=' => $last_of_month + ] + ]) + ->order(['record_date' => 'ASC']) + ->limit(1) + ->contain([]); + if($query->count() == 0) + { + // if any state balance for user exist, pick last one + $state_balances = $this->find('all') + ->where(['state_user_id' => $state_user_id]) + ->limit(1) + ->order(['record_date' => 'DESC']) + ; + // create one for first user transaction + if($state_balances->isEmpty()) + { + $state_user_transactions_table = TableRegistry::getTableLocator()->get('StateUserTransactions'); + $state_user_transaction = $state_user_transactions_table->find('all') + ->where(['state_user_id' => $state_user_id, 'StateUserTransactions.transaction_type_id <' => 3]) + ->contain(['Transactions' => ['TransactionCreations', 'TransactionSendCoins']]) + ->limit(1) + ->order(['transaction_id' => 'ASC']) + ->first() + ; + if(!$state_user_transaction) { + return null; + } + $entity = $this->newEntity(); + $entity->state_user_id = $state_user_id; + if($state_user_transaction->transaction_type_id == 1) { + $creation = $state_user_transaction->transaction->transaction_creations[0]; + $entity->amount = $creation->amount; + $entity->record_date = $creation->target_date; + } else if($state_user_transaction->transaction_type_id == 2) { + $transfer = $state_user_transaction->transaction->transaction_send_coins[0]; + $entity->amount = $transfer->amount; + $entity->record_date = $state_user_transaction->transaction->received; + } + if(!$this->save($entity)) { + return ['state' => 'error', 'msg' => 'error by saving state balance', 'details' => $entity->getErrors()]; + } + } + $state_balances = $this->find('all') + ->where(['state_user_id' => $state_user_id]) + ->limit(1) + ->order(['record_date' => 'DESC']) + ; + if($state_balances->count() == 1) + { + $current_state_balance = $state_balances->first(); + while(true) + { + $new_state_balance_begin = $this->calculateStateBalance($current_state_balance); + if(is_array($new_state_balance_begin)) { + return ['state' => 'error', 'msg' => 'error calculate state balance', 'details' => $new_state_balance_begin]; + } + $record_date = $new_state_balance_begin->record_date; + if($record_date->month === $month && $record_date->year === $year) { + return $new_state_balance_begin; + } + $current_state_balance = $this->updateLastStateBalanceOfMonth($month, $year, $state_user_id); + } + } + else + { + return ['state' => 'error', 'msg' => 'creation of first state_balance failes']; + } + } + return $query->first(); + } + + public function updateBalanceWithTransaction($newBalance, $recordDate, $userId) + { + // max 2 StateBalance Entrys per month: + // 1. first of month or first transaction of user + // 2. last of month or last transaction of user + $first_state_balance_of_month = $this->chooseForMonthAndUser($recordDate->month, $recordDate->year, $userId); + $updated_state_balance = null; + + if($first_state_balance_of_month == null || is_array($first_state_balance_of_month)) { + return $first_state_balance_of_month; + } + + if($first_state_balance_of_month->record_date->day == $recordDate->day && + $recordDate > $first_state_balance_of_month->record_date) { + if($first_state_balance_of_month->amount == $newBalance) { + // nothing to do here + return true; + } + $updated_state_balance = $first_state_balance_of_month; + $updated_state_balance->amount = $newBalance; + // copy complete record date, inclusive time + $first_state_balance_of_month->record_date = $recordDate; + } else { + $query = $this->find('all') + ->where(['AND' => [ + 'record_date >' => $first_state_balance_of_month->record_date, + 'record_date <=' => $recordDate, + 'state_user_id' => $userId + ]]); + if(!$query->isEmpty()) { + $updated_state_balance = $query->first(); + if($updated_state_balance->record_date == $recordDate) { + return true; + } + } else { + $updated_state_balance = $this->newEntity(); + $updated_state_balance->state_user_id = $userId; + } + $updated_state_balance->record_date = $recordDate; + $updated_state_balance->amount = $newBalance; + } + + if($updated_state_balance) { + if(!$this->save($updated_state_balance)) { + return ['state' => 'error', 'msg' => 'error by saving state balance', 'details' => $entity->getErrors()]; + } + + // delete all state_balances which came after + // they will be automaticlly recovered by next call of chooseForMonthAndUser + $this->deleteAll(['state_user_id' => $userId, 'record_date >' => $recordDate]); + } + + return true; + } } diff --git a/community_server/src/Model/Table/StateGroupAddressesTable.php b/community_server/src/Model/Table/StateGroupAddressesTable.php index 62fcf9470..a63e2ae1c 100644 --- a/community_server/src/Model/Table/StateGroupAddressesTable.php +++ b/community_server/src/Model/Table/StateGroupAddressesTable.php @@ -9,7 +9,7 @@ use Cake\Validation\Validator; /** * StateGroupAddresses Model * - * @property \App\Model\Table\StateGroupsTable&\Cake\ORM\Association\BelongsTo $StateGroups + * @property &\Cake\ORM\Association\BelongsTo $Groups * @property \App\Model\Table\AddressTypesTable&\Cake\ORM\Association\BelongsTo $AddressTypes * * @method \App\Model\Entity\StateGroupAddress get($primaryKey, $options = []) @@ -37,13 +37,13 @@ class StateGroupAddressesTable extends Table $this->setDisplayField('id'); $this->setPrimaryKey('id'); - $this->belongsTo('StateGroups', [ - 'foreignKey' => 'state_group_id', - 'joinType' => 'INNER' - ]); + /*$this->belongsTo('Groups', [ + 'foreignKey' => 'group_id', + 'joinType' => 'INNER', + ]);*/ $this->belongsTo('AddressTypes', [ 'foreignKey' => 'address_type_id', - 'joinType' => 'INNER' + 'joinType' => 'INNER', ]); } @@ -56,12 +56,13 @@ class StateGroupAddressesTable extends Table public function validationDefault(Validator $validator) { $validator - ->integer('id') + ->nonNegativeInteger('id') ->allowEmptyString('id', null, 'create'); $validator ->requirePresence('public_key', 'create') - ->notEmptyString('public_key'); + ->notEmptyString('public_key') + ->add('public_key', 'unique', ['rule' => 'validateUnique', 'provider' => 'table']); return $validator; } @@ -75,7 +76,8 @@ class StateGroupAddressesTable extends Table */ public function buildRules(RulesChecker $rules) { - $rules->add($rules->existsIn(['state_group_id'], 'StateGroups')); + $rules->add($rules->isUnique(['public_key'])); + //$rules->add($rules->existsIn(['group_id'], 'Groups')); $rules->add($rules->existsIn(['address_type_id'], 'AddressTypes')); return $rules; diff --git a/community_server/src/Model/Table/StateUserRolesTable.php b/community_server/src/Model/Table/StateUserRolesTable.php new file mode 100644 index 000000000..895e788b2 --- /dev/null +++ b/community_server/src/Model/Table/StateUserRolesTable.php @@ -0,0 +1,86 @@ +setTable('state_user_roles'); + $this->setPrimaryKey('id'); + + + $this->belongsTo('StateUser', [ + 'foreignKey' => 'state_user_id', + 'joinType' => 'INNER' + ]); + + $this->belongsTo('Role', [ + 'foreignKey' => 'role_id', + 'joinType' => 'INNER' + ]); + + + } + + /** + * Default validation rules. + * + * @param \Cake\Validation\Validator $validator Validator instance. + * @return \Cake\Validation\Validator + */ + public function validationDefault(Validator $validator) + { + $validator + ->integer('id') + ->allowEmptyString('id', null, 'create'); + + + + return $validator; + } + + /** + * Returns a rules checker object that will be used for validating + * application integrity. + * + * @param \Cake\ORM\RulesChecker $rules The rules object to be modified. + * @return \Cake\ORM\RulesChecker + */ + public function buildRules(RulesChecker $rules) + { + // $rules->add($rules->existsIn(['index_id'], 'Indices')); + //$rules->add($rules->existsIn(['state_group_id'], 'StateGroups')); + + return $rules; + } + + +} diff --git a/community_server/src/Model/Table/TransactionGroupAddaddressTable.php b/community_server/src/Model/Table/TransactionGroupAddaddressTable.php index 3fd03f32a..cb9912083 100644 --- a/community_server/src/Model/Table/TransactionGroupAddaddressTable.php +++ b/community_server/src/Model/Table/TransactionGroupAddaddressTable.php @@ -45,6 +45,10 @@ class TransactionGroupAddaddressTable extends Table 'foreignKey' => 'address_type_id', 'joinType' => 'INNER' ]); + $this->belongsTo('StateUsers', [ + 'foreignKey' => 'state_user_id', + 'joinType' => 'INNER' + ]); } /** @@ -77,6 +81,7 @@ class TransactionGroupAddaddressTable extends Table { $rules->add($rules->existsIn(['transaction_id'], 'Transactions')); $rules->add($rules->existsIn(['address_type_id'], 'AddressTypes')); + $rules->add($rules->existsIn(['state_user_id'], 'StateUsers')); return $rules; } diff --git a/community_server/src/Model/Transactions/Record.php b/community_server/src/Model/Transactions/Record.php new file mode 100644 index 000000000..862399af5 --- /dev/null +++ b/community_server/src/Model/Transactions/Record.php @@ -0,0 +1,575 @@ +signature = $signature; + $this->publicKey = $pubkey; + } + + public function finalize($transactionId) + { + $signaturesTable = TableRegistry::getTableLocator()->get('TransactionSignatures'); + $entity = $signaturesTable->newEntity(); + $entity->transaction_id = $transactionId; + if(strlen($this->signature) != 128) { + return ['state' => 'error', 'msg' => 'invalid signature size', 'details' => strlen($this->signature)]; + } + if(strlen($this->publicKey) != 64) { + return ['state' => 'error', 'msg' => 'invalid pubkey size', 'details' => strlen($this->publicKey)]; + } + if(!preg_match('/^[0-9a-fA-F]*$/', $this->signature)) { + return ['state' => 'error', 'msg' => 'signature isn\'t in hex format']; + } + if(!preg_match('/^[0-9a-fA-F]*$/', $this->publicKey)) { + return ['state' => 'error', 'msg' => 'publicKey isn\'t in hex format']; + } + $entity->signature = hex2bin($this->signature); + $entity->pubkey = hex2bin($this->publicKey); + + if(!$signaturesTable->save($entity)) { + return ['state' => 'error', 'msg' => 'error saving signature', 'details' => $entity->getErrors()]; + } + return true; + } +} + + + +class GradidoModifieUserBalance +{ + private $state_users = []; + private $user_balances = []; + + public function getUserId($userPublicKey) + { + $stateUsersTable = TableRegistry::getTableLocator()->get('StateUsers'); + + $stateUser = $stateUsersTable->find('all')->where(['public_key' => hex2bin($userPublicKey)]); + if($stateUser->isEmpty()) { + return ['state' => 'error', 'msg' => '[GradidoModifieUserBalance::getUserId] couldn\'t find user via public key']; + } + $id = $stateUser->first()->id; + if($id && is_int($id) && (int)$id > 0 && !in_array((int)$id, $this->state_users)) { + array_push($this->state_users, (int)$id); + } + return $id; + } + + public function updateBalance($newBalance, $recordDate, $userId) + { + $stateBalancesTable = TableRegistry::getTableLocator()->get('StateBalances'); + $state_balance_query = $stateBalancesTable->find()->where(['state_user_id' => $userId])->order(['record_date ASC']); + $state_balance = null; + if($state_balance_query->count() > 0) { + $state_balance = $state_balance_query->last(); + } else { + $state_balance = $stateBalancesTable->newEntity(); + $state_balance->state_user_id = $userId; + } + $state_balance->amount = $newBalance; + $state_balance->record_date = $recordDate; + $this->user_balances[$userId] = $state_balance; + $stateBalancesTable->save($state_balance); + return true; + //$this->user_balances[$userId] = ['balance' => $newBalance, ''] + // + //return $stateBalancesTable->updateBalanceWithTransaction($newBalance, $recordDate, $userId); + + /*$first_of_month = new Time("$year-$month-01 00:00"); + $stateBalanceQuery = $stateBalancesTable + ->find('all') + ->where(['state_user_id' => $userId]) + ->order(['record_date' => 'DESC']) + ->limit(1); + $entity = null; + + if(!$stateBalanceQuery->isEmpty()) { + $entity = $stateBalanceQuery->first(); + if($entity->record_date != NULL && + ($entity->record_date > $recordDate || $entity->record_date->day == 1)) { + return false; + } + } else { + $entity = $stateBalancesTable->newEntity(); + $entity->state_user_id = $userId; + } + $entity->record_date = $recordDate; + $entity->amount = $newBalance; + /*if(!$stateBalancesTable->save($entity)) { + return ['state' => 'error', 'msg' => 'error saving state balance', 'details' => $entity->getErrors()]; + }*/ + //return true; + } + + public function getAllStateUsers() + { + return $this->state_users; + } + public function getAllStateUserBalances() + { + return $this->user_balances; + } +} + +class ManageNodeGroupAdd extends GradidoModifieUserBalance +{ + /* + "add_user": { + "user\": " << user << ", + }, + OR + + "move_user_inbound|move_user_outbound": { + "user": " << user << ", + "other_group": " << other_group << ", + "paired_transaction_id": { + "seconds": << ts.seconds <<, + "nanos": << ts.nanos + } + }, + + */ + + private $user_pubkey; + private $other_group = ''; + private $remove_from_group = false; + + public function __construct($data) + { + $this->user_pubkey = $data['user']; + if(isset($data['other_group'])) { + $this->other_group = $data['other_group']; + } + } + + public function finalize($transactionId, $received) + { + $transactionGroupAddadressTable = TableRegistry::getTableLocator()->get('TransactionGroupAddaddress'); + $stateGroupAddresses = TableRegistry::getTableLocator()->get('StateGroupAddresses'); + $transactionGroupEntity = $transactionGroupAddadressTable->newEntity(); + if(!is_int($transactionId)) { + return ['state' => 'error', 'msg' => '[ManageNodeGroupAdd::finalize] transaction id is not int', 'details' => $transactionId]; + } + $transactionGroupEntity->transaction_id = $transactionId; + $transactionGroupEntity->address_type_id = 1; + if(strlen($this->user_pubkey) != 64) { + return ['state' => 'error', 'msg' => 'invalid size user pubkey', 'details' => strlen($this->user_pubkey)]; + } + if(!preg_match('/^[0-9a-fA-F]*$/', $this->user_pubkey)) { + return ['state' => 'error', 'msg' => 'user_pubkey isn\'t in hex format']; + } + + $userPubkeyBin = hex2bin($this->user_pubkey); + + $transactionGroupEntity->public_key = $userPubkeyBin; + $user_id = $this->getUserId($this->user_pubkey); + if(!is_int($user_id)) { + return ['state' => 'error', 'msg' => '[ManageNodeGroupAdd::finalize] user id is not int', 'details' => $user_id]; + } + $transactionGroupEntity->state_user_id = $user_id; + $transactionGroupEntity->remove_from_group = $this->remove_from_group; + if(!$transactionGroupAddadressTable->save($transactionGroupEntity)) { + return ['state' => 'error', 'msg' => 'error saving TransactionGroupAddaddress Entity', 'details' => $transactionGroupEntity->getErrors()]; + } + + + if($this->remove_from_group) { + $stateGroup_query = $stateGroupAddresses->find('all')->where(['public_key' => hex2bin($this->user_pubkey)]); + if(!$stateGroup_query->isEmpty()) { + $stateGroupAddresses->delete($stateGroup_query->first()); + } + } else { + $stateGroupAddressesEntity = $stateGroupAddresses->newEntity(); + $stateGroupAddressesEntity->group_id = 1; + $stateGroupAddressesEntity->public_key = $userPubkeyBin; + $stateGroupAddressesEntity->address_type_id = 1; + if(!$stateGroupAddresses->save($stateGroupAddressesEntity)) { + return ['state' => 'error', 'msg' => 'error saving state group addresses entity', 'details' => $stateGroupAddressesEntity->getErrors()]; + } + } + + return true; + } + + public function setRemoveFromGroup($removeFromGroup) { + $this->remove_from_group = $removeFromGroup; + } +} + + +class GradidoCreation extends GradidoModifieUserBalance +{ + /* + * "gradido_creation": { + "user": " << user << ", + "new_balance": << v.new_balance << , + "prev_transfer_rec_num": << v.prev_transfer_rec_num <<, + "amount": << v.amount << + } + */ + private $userPubkey; + private $amount; + private $targetDate; // seems currently not in node server implementet, use hedera date until it is implemented + private $new_balance; + + + public function __construct($data) + { + $this->userPubkey = $data['user']; + $this->amount = $data['amount']['amount']; + $this->new_balance = $data['new_balance']['amount']; + //$this->targetDate = $received; + } + + public function finalize($transactionId, $received) + { + // TODO: don't use, after node server transmit correct date + $this->targetDate = $received; + + $transactionCreationTable = TableRegistry::getTableLocator()->get('TransactionCreations'); + + + $state_user_id = $this->getUserId($this->userPubkey); + if(!is_int($state_user_id)) { + return $state_user_id; + } + + $entity = $transactionCreationTable->newEntity(); + $entity->transaction_id = $transactionId; + $entity->amount = $this->amount; + $entity->target_date = $this->targetDate; + $entity->state_user_id = $state_user_id; + + if(!$transactionCreationTable->save($entity)) { + return ['state' => 'error', 'msg' => 'error saving create transaction', 'details' => $entity->getErrors()]; + } + + $balance_result = $this->updateBalance($this->new_balance, $received, $state_user_id); + if(is_array($balance_result)) { + return $balance_result; + } + + return true; + } + + + +} + +class GradidoTransfer extends GradidoModifieUserBalance +{ + /* + "local_transfer|inbound_transfer|outbound_transfer": { + "sender": { + "user": " << sender << ", + "new_balance": << tt.sender.new_balance << , + "prev_transfer_rec_num": << tt.sender.prev_transfer_rec_num << + }, + "receiver": { + "user": " << receiver << ", + "new_balance": << tt.receiver.new_balance << , + "prev_transfer_rec_num": << tt.receiver.prev_transfer_rec_num << + }, + "amount": << tt.amount << + }, + * */ + private $amount; + private $sender_new_balance = null; + private $sender_pubkey; + + private $receiver_pubkey; + private $receiver_new_balance = null; + + + public function __construct($data) + { + $this->amount = $data['amount']['amount']; + + $sender = $data['sender']; + $this->sender_pubkey = $sender['user']; + if(isset($sender['new_balance'])) { + $this->sender_new_balance = $sender['new_balance']['amount']; + } + + $receiver = $data['receiver']; + $this->receiver_pubkey = $receiver['user']; + if(isset($receiver['new_balance'])) { + $this->receiver_new_balance = $receiver['new_balance']['amount']; + } + + } + + public function finalize($transactionId, $received) + { + $transactionTransferTable = TableRegistry::getTableLocator()->get('TransactionSendCoins'); + if(strlen($this->sender_pubkey) != 64) { + return ['state' => 'error', 'msg' => 'invalid size sender pubkey', 'details' => strlen($this->user_pubkey)]; + } + if(!preg_match('/^[0-9a-fA-F]*$/', $this->sender_pubkey)) { + return ['state' => 'error', 'msg' => 'sender_pubkey isn\'t in hex format']; + } + if(strlen($this->receiver_pubkey) != 64) { + return ['state' => 'error', 'msg' => 'invalid size receiver pubkey', 'details' => strlen($this->user_pubkey)]; + } + if(!preg_match('/^[0-9a-fA-F]*$/', $this->receiver_pubkey)) { + return ['state' => 'error', 'msg' => 'receiver_pubkey isn\'t in hex format']; + } + + $sender_id = $this->getUserId($this->sender_pubkey); + $receiver_id = $this->getUserId($this->receiver_pubkey); + if(is_array($sender_id) && is_array($receiver_id)) { + return ['state' => 'error', 'msg' => 'neither sender or receiver known']; + } + $transferEntity = $transactionTransferTable->newEntity(); + $transferEntity->transaction_id = $transactionId; + $transferEntity->sender_public_key = hex2bin($this->sender_pubkey); + $transferEntity->receiver_public_key = hex2bin($this->receiver_pubkey); + $transferEntity->amount = $this->amount; + if($this->sender_new_balance != null) { + $transferEntity->sender_final_balance = $this->sender_new_balance; + + if(is_int($sender_id) && $sender_id > 0) { + $transferEntity->state_user_id = $sender_id; + $balance_result = $this->updateBalance($this->sender_new_balance, $received, $sender_id); + if(is_array($balance_result)) { + return $balance_result; + } + } + } + if($this->receiver_new_balance != null && is_int($receiver_id) && $receiver_id > 0) { + $transferEntity->receiver_user_id = $receiver_id; + $balance_result = $this->updateBalance($this->receiver_new_balance, $received, $receiver_id); + if(is_array($balance_result)) { + return $balance_result; + } + } + + if(!$transactionTransferTable->save($transferEntity)) { + return ['state' => 'error', 'msg' => 'error saving transaction send coins entity', 'details' => $transferEntity->getErrors()]; + } + + return true; + } + +} + + + + +class Record +{ + private $sequenceNumber = 0; + private $runningHash = null; + private $transactionType = ''; + private $memo = ''; + private $signatures = []; + private $received; + private $transactionObj = null; + private $result; + private $partCount = 0; + + public function __construct() + { + + } + + + public function parseRecord($json) { + if(!isset($json['record_type'])) { + return false; + } + //var_dump($json); + switch($json['record_type']) { + case 'GRADIDO_TRANSACTION': + return $this->parseTransaction($json['transaction']); + case 'MEMO': + $this->memo .= $json['memo']; + return true; + case 'SIGNATURES': + return $this->parseSignatures($json['signature']); + case 'STRUCTURALLY_BAD_MESSAGE': + case 'RAW_MESSAGE': + case 'BLANK': + return false; + } + } + + /*! + * \brief save data parts in db + */ + public function finalize() + { + $transactionTypesTable = TableRegistry::getTableLocator()->get('TransactionTypes'); + $transactionsTable = TableRegistry::getTableLocator()->get('Transactions'); + $stateUserTransactionsTable = TableRegistry::getTableLocator()->get('StateUserTransactions'); + + $transactionTypeName = $this->nodeTransactionTypeToDBTransactionType($this->transactionType); + $transactionTypeResults = $transactionTypesTable->find('all')->where(['name' => $transactionTypeName]); + if($transactionTypeResults->isEmpty()) { + return [ + 'state' => 'error', 'msg' => 'transaction type not found', + 'details' => ['nodeType' => $this->transactionType, 'dbType' => $transactionTypeName] + ]; + } + if(!$this->transactionObj) { + return ['state' => 'error', 'msg' => 'transaction obj is null']; + } + if($this->sequenceNumber <= 0) { + return ['state' => 'error', 'msg' => 'sequence number invalid', 'details' => $this->sequenceNumber]; + } + $transactionExistResult = $transactionsTable->find('all')->where(['id' => intval($this->sequenceNumber)]); + if(!$transactionExistResult->isEmpty()) { + return ['state' => 'warning', 'msg' => 'transaction already exist in db', 'details' => $this->sequenceNumber]; + } + $newTransaction = $transactionsTable->newEntity(); + $newTransaction->id = $this->sequenceNumber; + $newTransaction->transaction_type_id = $transactionTypeResults->first()->id; + $newTransaction->memo = $this->memo; + if($this->runningHash != '' && strlen($this->runningHash) % 2 == 0) { + $newTransaction->tx_hash = hex2bin($this->runningHash); + } + $newTransaction->received = $this->received; + + //! TODO change into transaction, if at least one fail, rollback + /* + // In a controller. + $articles->getConnection()->transactional(function () use ($articles, $entities) { + foreach ($entities as $entity) { + $articles->save($entity, ['atomic' => false]); + } + }); + */ + if(!$transactionsTable->save($newTransaction)) { + return ['state' => 'error', 'msg' => 'error saving transaction', 'details' => $newTransaction->getErrors()]; + } + + foreach($this->signatures as $sign) { + $sign_result = $sign->finalize($this->sequenceNumber); + if($sign_result !== true) { + return ['state' => 'error', 'msg', 'error finalizing signature', 'details' => $sign_result]; + } + } + $transaction_obj_result = $this->transactionObj->finalize($newTransaction->id, $this->received); + if($transaction_obj_result !== true) { + return ['state' => 'error', 'msg' => 'error finalizing transaction object', 'details' => $transaction_obj_result]; + } + $state_users = $this->transactionObj->getAllStateUsers(); + $sut_entities = []; + $state_user_balances = $this->transactionObj->getAllStateUserBalances(); + foreach($state_users as $state_user_id) { + $entity = $stateUserTransactionsTable->newEntity(); + $entity->state_user_id = $state_user_id; + $entity->transaction_id = $newTransaction->id; + $entity->transaction_type_id = $newTransaction->transaction_type_id; + $entity->balance = $state_user_balances[$state_user_id]->amount; + $entity->balance_date = $state_user_balances[$state_user_id]->record_date; + $sut_entities[] = $entity; + } + $sut_results = $stateUserTransactionsTable->saveMany($sut_entities); + foreach($sut_results as $i => $result) { + if(false == $result) { + return ['state' => 'error', 'msg' => 'error saving state_user_transaction', 'details' => $sut_entities[$i]->getErrors()]; + } + } + + return true; + + } + + private function nodeTransactionTypeToDBTransactionType($nodeTransactionType) + { + switch($nodeTransactionType) { + case 'GRADIDO_CREATION': + return 'creation'; + + case 'MOVE_USER_INBOUND': + case 'ADD_USER': + return 'group add member'; + + case 'MOVE_USER_OUTBOUND': + return 'group remove member'; + + case 'LOCAL_TRANSFER': + case 'INBOUND_TRANSFER': + case 'OUTBOUND_TRANSFER': + return 'transfer'; + } + return 'unknown'; + } + + private function parseSignatures($signaturesArray) + { + foreach($signaturesArray as $sign) { + $this->signatures[] = new Signature($sign['signature'], $sign['pubkey']); + } + return true; + } + + private function parseTransaction($data) + { + $this->transactionType = $data['transaction_type']; + $sign = $data['signature']; + $this->signatures[] = new Signature($sign['signature'], $sign['pubkey']); + + $hedera = $data['hedera_transaction']; + $this->sequenceNumber = $hedera['sequenceNumber']; + $this->runningHash = $hedera['runningHash']; + $this->received = Time::createFromTimestamp($hedera['consensusTimestamp']['seconds']); + + $field_index = ''; + $class_name = ''; + + $removeFromGroup = false; + switch($this->transactionType) + { + case 'GRADIDO_CREATION': $field_index = 'gradido_creation'; $class_name = 'GradidoCreation'; break; + case 'ADD_USER': $field_index = 'add_user'; $class_name = 'ManageNodeGroupAdd'; break; + case 'MOVE_USER_INBOUND': $field_index = 'move_user_inbound'; $class_name = 'ManageNodeGroupAdd'; break; + case 'MOVE_USER_OUTBOUND': $field_index = 'move_user_outbound'; $class_name = 'ManageNodeGroupAdd'; $removeFromGroup = true; break; + case 'LOCAL_TRANSFER': $field_index = 'local_transfer'; $class_name = 'GradidoTransfer'; break; + case 'INBOUND_TRANSFER': $field_index = 'inbound_transfer'; $class_name = 'GradidoTransfer'; break; + case 'OUTBOUND_TRANSFER': $field_index = 'outbound_transfer'; $class_name = 'GradidoTransfer'; break; + } + if($class_name == '' || $field_index == '') { + return ['state' => 'error', 'msg' => 'node transaction type unknown', 'details' => $this->transactionType]; + } + $class_name = 'Model\\Transactions\\' . $class_name; + $this->transactionObj = new $class_name($data[$field_index]); + if($class_name == 'ManageNodeGroupAdd') { + $this->transactionObj->setRemoveFromGroup($removeFromGroup); + } + + $this->result = $data['result']; + $this->partCount = intval($data['parts']); + $this->memo = $data['memo']; + return true; + } + + public function getSequenceNumber() { + return $this->sequenceNumber; + } + public function getPartCount() { + return $this->partCount; + } + +} \ No newline at end of file diff --git a/community_server/src/Template/AddressTypes/add.ctp b/community_server/src/Template/AddressTypes/add.ctp index 7422b666a..f62c5115e 100644 --- a/community_server/src/Template/AddressTypes/add.ctp +++ b/community_server/src/Template/AddressTypes/add.ctp @@ -4,8 +4,8 @@ * @var \App\Model\Entity\AddressType $addressType */ ?> -