2021-03-18 12:55:04 +00:00

194 lines
7.3 KiB
PHP

<?php
namespace Model\Transactions;
use Cake\ORM\TableRegistry;
use Cake\I18n\Date;
class TransactionBody extends TransactionBase {
private $mProtoTransactionBody = null;
private $mSpecificTransaction = null;
private $mTransactionID = 0;
private $transactionTypeId = 0;
public function __construct($bodyBytes) {
$this->mProtoTransactionBody = new \Model\Messages\Gradido\TransactionBody();
try {
$this->mProtoTransactionBody->mergeFromString($bodyBytes);
// cannot catch Exception with cakePHP, I don't know why
} catch(\Google\Protobuf\Internal\GPBDecodeException $e) {
//var_dump($e);
$this->addError('TransactionBody', $e->getMessage());
return;
}
switch($this->mProtoTransactionBody->getData()) {
case 'creation' : $this->mSpecificTransaction = new TransactionCreation($this->mProtoTransactionBody->getCreation()); break;
case 'transfer' : $this->mSpecificTransaction = new TransactionTransfer($this->mProtoTransactionBody->getTransfer()); break;
}
}
public function validate($sigPairs) {
// transaction type id
$transactionTypesTable = TableRegistry::getTableLocator()->get('transaction_types');
$typeName = $this->getTransactionTypeName();
$transactionType = $transactionTypesTable->find('all')->where(['name' => $typeName])->select(['id'])->first();
if($transactionType == NULL) {
$this->addError('TransactionBody::validate', 'zero type id for type: ' . $typeName);
return false;
}
$this->transactionTypeId = $transactionType->id;
// check if creation time is in the past
if($this->mProtoTransactionBody->getCreated()->getSeconds() > time()) {
$this->addError('TransactionBody::validate', 'Transaction were created in the past!');
return false;
}
if(!$this->mSpecificTransaction->validate($sigPairs)) {
$this->addErrors($this->mSpecificTransaction->getErrors());
return false;
}
return true;
}
public function getSpecificTransaction() {
return $this->mSpecificTransaction;
}
public function getMemo() {
return $this->mProtoTransactionBody->getMemo();
}
public function getTransactionTypeName()
{
return $this->mProtoTransactionBody->getData();
}
public function save($firstPublic, $sigMap) {
$transactionsTable = TableRegistry::getTableLocator()->get('transactions');
$transactionEntity = $transactionsTable->newEntity();
$transactionEntity->transaction_type_id = $this->transactionTypeId;
$transactionEntity->memo = $this->getMemo();
$transactionEntity->received = new Date();
if ($transactionsTable->save($transactionEntity)) {
// success
$this->mTransactionID = $transactionEntity->id;
if(!$this->mSpecificTransaction->save($transactionEntity->id, $firstPublic, $transactionEntity->received)) {
$this->addErrors($this->mSpecificTransaction->getErrors());
return false;
}
} else {
$this->addError('TransactionBody::save', 'error saving transaction with: ' . json_encode($transactionEntity->getError()));
return false;
}
$previousTxHash = null;
if($this->mTransactionID > 1) {
try {
$previousTransaction = $transactionsTable
->find('all', ['contain' => false])
->select(['tx_hash'])
->where(['id' => $this->mTransactionID - 1])
->first();
/*$previousTransaction = $transactionsTable->get($this->mTransactionID - 1, [
'contain' => false,
'fields' => ['tx_hash']
]);*/
} catch(Cake\Datasource\Exception\RecordNotFoundException $ex) {
$this->addError('TransactionBody::save', 'previous transaction (with id ' . ($this->mTransactionID-1) . ' not found');
return false;
}
if(!$previousTransaction) {
// shouldn't occur
$this->addError('TransactionBody::save', 'previous transaction (with id ' . ($this->mTransactionID-1) . ' not found');
return false;
}
$previousTxHash = $previousTransaction->tx_hash;
}
try {
//$transactionEntity->received = $transactionsTable->get($transactionEntity->id, ['contain' => false, 'fields' => ['received']])->received;
$transactionEntity->received = $transactionsTable
->find('all', ['contain' => false])
->where(['id' => $transactionEntity->id])
->select(['received'])->first()->received;
} catch(Cake\Datasource\Exception\RecordNotFoundException $ex) {
$this->addError('TransactionBody::save', 'current transaction (with id ' . ($transactionEntity->id) . ' not found');
$this->addError('exception: ', $ex->getMessage());
return false;
}
// calculate tx hash
// previous tx hash + id + received + sigMap as string
// Sodium use for the generichash function BLAKE2b today (11.11.2019), mabye change in the future
$state = \Sodium\crypto_generichash_init();
//echo "prev hash: $previousTxHash\n";
if($previousTxHash != null) {
\Sodium\crypto_generichash_update($state, stream_get_contents($previousTxHash));
}
//echo "id: " . $transactionEntity->id . "\n";
\Sodium\crypto_generichash_update($state, strval($transactionEntity->id));
//echo "received: " . $transactionEntity->received;
\Sodium\crypto_generichash_update($state, $transactionEntity->received->i18nFormat('yyyy-MM-dd HH:mm:ss'));
\Sodium\crypto_generichash_update($state, $sigMap->serializeToString());
$transactionEntity->tx_hash = \Sodium\crypto_generichash_final($state);
if ($transactionsTable->save($transactionEntity)) {
return true;
}
$this->addError('TransactionBody::save', 'error saving transaction with: ' . json_encode($transactionEntity->getError()));
return false;
}
public function getTransactionID() {
return $this->mTransactionID;
}
public function getTransactionTypeId() {
return $this->transactionTypeId;
}
static public function fromEntity($memo, $transaction)
{
$protoBody = new \Model\Messages\Gradido\TransactionBody();
$protoBody->setMemo($memo);
//$created->setSeconds($var);
//$protoBody->setCreated($created);
if(count($transaction->transaction_creations) == 1) {
//echo "is creation<br>";
$protoBody->setCreation(TransactionCreation::fromEntity($transaction->transaction_creations[0])->getProto());
}
else if(count($transaction->transaction_send_coins) == 1) {
//echo "is transfer";
$protoBody->setTransfer(TransactionTransfer::fromEntity($transaction->transaction_send_coins)->getProto());
} else {
return ['invalid transaction type or count'];
}
return $protoBody;
}
static public function build($memo, $specificTransaction)
{
$protoBody = new \Model\Messages\Gradido\TransactionBody();
$protoBody->setMemo($memo);
if(is_a($specificTransaction, 'TransactionCreation')) {
$protoBody->setCreation($specificTransaction->getProto());
} else if(is_a($specificTransaction, 'TransactionTransfer')) {
$protoBody->setTransfer($specificTransaction->getProto());
} else {
return ['invalid tarnsaction type'];
}
return $protoBody;
}
}