Commits (2)
......@@ -2,18 +2,43 @@
namespace App\Controller;
use Doctrine\DBAL\ParameterType;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use PDO;
class TransactionController extends AbstractController
{
const TxPerPage = 50;
const TransactionDetails = "SELECT t.height, t.type, t.subtype, t.version, t.full_hash, t.signature, t.amount, t.fee,
FALSE AS mo, FALSE AS mos, -- Will be set later in PHP
getTransactionType(t.type, t.subtype) AS type_name,
(t.timestamp + 1407722400) AS unixtime,
(t.block_timestamp + 1407722400) AS block_unixtime,
s.name AS sender_name,
r.name AS recipient_name,
CAST(t.sender_id AS UNSIGNED) AS sender_id,
CAST(t.recipient_id AS UNSIGNED) AS recipient_id,
CAST(t.id AS UNSIGNED) AS id,
CAST(t.block_id AS UNSIGNED) AS block_id,
t.db_id
FROM transaction t
LEFT JOIN ec_account s ON (t.sender_id = s.id)
LEFT JOIN ec_account r ON (t.recipient_id = r.id)
WHERE CAST(t.id AS UNSIGNED) = ?";
const TransactionMoDetails = " SELECT mo.recipient_id, mo.amount, r.name AS recipient_name
FROM mo
LEFT JOIN ec_account r ON (r.id = mo.recipient_id)
WHERE mo.db_id = ?";
const TransactionMosDetails = " SELECT mosr.recipient_id, r.name AS recipient_name
FROM mosr
LEFT JOIN ec_account r ON (r.id = mosr.recipient_id)
WHERE mosr.db_id = ?";
const TransactionsByHeight = " SELECT x.id, x.sender_id, x.recipient_id, x.fee, x.amount, x.unixtime,
x.transaction_type, s.name AS sender_name, r.name AS recipient_name
FROM (
......@@ -91,20 +116,6 @@ class TransactionController extends AbstractController
LEFT JOIN ec_account r ON (x.recipient_id = r.id)
WHERE x.recipient_id IS NOT NULL -- TransactionController/TransactionsByBlockId";
const TransactionCountByAccountId = "
SELECT SUM(count) AS count FROM (
-- One To One
SELECT count(t.id) AS count FROM transaction t WHERE (CAST(t.sender_id AS UNSIGNED) = ? OR CAST(t.recipient_id AS UNSIGNED) = ?) AND t.type = 0 AND t.subtype = 0 AND t.recipient_id IS NOT NULL
UNION -- MultiOut IN
SELECT count(mo.recipient_id) AS count FROM mo LEFT JOIN transaction t ON (mo.db_id = t.db_id) WHERE CAST(mo.recipient_id AS UNSIGNED) = ?
UNION -- MultiOut OUT
SELECT count(mo.recipient_id) AS count FROM transaction t JOIN mo ON (t.db_id = mo.db_id) WHERE CAST(t.sender_id AS UNSIGNED) = ? AND t.type = 0 AND t.subtype = 1
UNION -- MultiOutSame IN
SELECT count(mosr.recipient_id) AS count FROM mosr LEFT JOIN transaction t ON (mosr.db_id = t.db_id AND t.type = 0 AND t.subtype = 2) WHERE CAST(mosr.recipient_id AS UNSIGNED) = ?
UNION -- MultiOutSame OUT
SELECT count(mosr.recipient_id) AS count FROM transaction t LEFT JOIN mosr ON (mosr.db_id = t.db_id AND t.type = 0 AND t.subtype = 2) WHERE CAST(t.sender_id AS UNSIGNED) = ?
) as x";
const TransactionByAccountId = "SELECT x.id, x.sender_id, x.recipient_id, x.fee, x.amount, x.unixtime,
x.transaction_type, x.block_id, x.height, s.name AS sender_name, r.name as recipient_name
FROM ( -- One to One transactions, Pool: 7,8s,
......@@ -189,110 +200,158 @@ class TransactionController extends AbstractController
}
/**
* Returns Transaction Details
*
* @param $id
* @param $page
* @Route("/transactions/byHeight/{id}/{page}", name="transactions_by_height", defaults={"page"=0})
* @throws Exception
* @return Response
* @Route("/transaction/{id}", name="transaction_details")
* @
*/
public function getBlockTransactionsByHeight($id, $page) {
// Height is called ID here to use a unified transactionlist template
$start = $page * self::TxPerPage;
public function getTransaction($id) {
$con = $this->entityManager->getConnection();
$stmt = $con->prepare(self::TransactionsByHeight);
$stmt = $con->prepare(self::TransactionDetails);
$stmt->bindValue(1, $id);
$stmt->bindValue(2, $id);
$stmt->bindValue(3, $id);
$stmt->execute();
$transactions = $stmt->fetchAll();
$Data = $this->paginationCalculator(count($transactions), $page);
$showTransactions = [];
for($i = $start; $i < $start + self::TxPerPage; $i++) {
if(isset($transactions[$i])) {
$showTransactions[] = $transactions[$i];
$transaction = $stmt->fetch();
// Get MultiOut Recipients
if($transaction['type'] == 0 && $transaction['subtype'] == 1) {
$transaction['mo'] = true;
$stmt = $con->prepare(self::TransactionMoDetails);
$stmt->bindValue(1, $transaction['db_id']);
$stmt->execute();
$transaction['mo_details'] = $stmt->fetchAll();
}
// Get MultiOutSame Recipients
elseif($transaction['type'] == 0 && $transaction['subtype'] == 2) {
$transaction['mos'] = true;
$stmt = $con->prepare(self::TransactionMosDetails);
$stmt->bindValue(1, $transaction['db_id']);
$stmt->execute();
$mos = $stmt->fetchAll();
$amount = $transaction['amount'] / $stmt->rowCount();
foreach($mos as $MultiOutSame) {
$MultiOutSame['amount'] = $amount;
$transaction['mo_details'][] = $MultiOutSame;
}
}
$Data['transactions'] = $showTransactions;
$Data['route'] = 'transactions_by_height';
$Data['id'] = $id;
return $this->render('transaction.details.html.twig', [
'transaction' => $transaction,
]);
}
return $this->render('transaction.list.html.twig', $Data);
/**
* Wrapper for getBlockTransactions for Tx by Height
*
* @param $id
* @param $page
* @Route("/transactions/byHeight/{id}/{page}", name="transactions_by_height", defaults={"page"=0})
* @return Response
* @throws Exception
*/
public function getBlockTransactionsByHeight($id, $page)
{
return $this->getBlockTransactions($id, $page, 'height');
}
/**
* Wrapper for getBlockTransactions for Tx by ID
*
* @param $id
* @param $page
* @Route("/transactions/byBlockId/{id}/{page}", name="transactions_by_blockId", defaults={"page"=0})
* @return Response
* @throws Exception
*/
public function getBlockTransactionsByBlockId($id, $page)
{
return $this->getBlockTransactions($id, $page, 'id');
}
/**
* @param $id
* @param $page
* @return Response
* @throws Exception
* @Route("/transactions/byAccountId/{id}/{page}", name="transactions_by_accountId", defaults={"page"=0})
*/
public function getBlockTransactionsByBlockId($id, $page) {
public function getAccountTransactionsByAccountId($id, $page)
{
$start = $page * self::TxPerPage;
$con = $this->entityManager->getConnection();
$stmt = $con->prepare(self::TransactionsByBlockId);
$stmt = $con->prepare(self::TransactionByAccountId);
$stmt->bindValue(1, $id);
$stmt->bindValue(2, $id);
$stmt->bindValue(3, $id);
$stmt->bindValue(4, $id);
$stmt->bindValue(5, $id);
$stmt->bindValue(6, $id);
$stmt->execute();
$transactions = $stmt->fetchAll();
$Data = $this->paginationCalculator(count($transactions), $page);
// As we cannot limit + offset via SQL and use this number for pagination calculation, we have to limit + offset here
$showTransactions = [];
for($i = $start; $i < $start + self::TxPerPage; $i++) {
if(isset($transactions[$i])) {
for ($i = $start; $i < $start + self::TxPerPage; $i++) {
if (isset($transactions[$i])) {
$showTransactions[] = $transactions[$i];
}
}
$Data['transactions'] = $showTransactions;
$Data['route'] = 'transactions_by_blockId';
$Data['route'] = 'transactions_by_accountId';
$Data['id'] = $id;
return $this->render('transaction.list.html.twig', $Data);
}
/**
* General Method for retreival of Transactions of a Block
*
* @param $id
* @param $page
* @param $type
* @return Response
* @throws Exception
* @Route("/transactions/byAccountId/{id}/{page}", name="transactions_by_accountId", defaults={"page"=0})
*/
public function getAccountTransactionsByAccountId($id, $page) {
$start = $page * self::TxPerPage;
private function getBlockTransactions($id, $page, $type)
{
if ($type == 'id') {
$query = self::TransactionsByBlockId;
$route = 'transactions_by_blockId';
} else {
$query = self::TransactionsByHeight;
$route = 'transactions_by_height';
}
$start = $page * self::TxPerPage;
$con = $this->entityManager->getConnection();
$stmt = $con->prepare(self::TransactionByAccountId);
$stmt = $con->prepare($query);
$stmt->bindValue(1, $id);
$stmt->bindValue(2, $id);
$stmt->bindValue(3, $id);
$stmt->bindValue(4, $id);
$stmt->bindValue(5, $id);
$stmt->bindValue(6, $id);
$stmt->execute();
$transactions = $stmt->fetchAll();
$Data = $this->paginationCalculator(count($transactions), $page);
// As we cannot limit + offset via SQL and use this number for pagination calculation, we have to limit + offset here
$showTransactions = [];
for($i = $start; $i < $start + self::TxPerPage; $i++) {
if(isset($transactions[$i])) {
for ($i = $start; $i < $start + self::TxPerPage; $i++) {
if (isset($transactions[$i])) {
$showTransactions[] = $transactions[$i];
}
}
$Data['transactions'] = $showTransactions;
$Data['route'] = 'transactions_by_accountId';
$Data['route'] = $route;
$Data['id'] = $id;
return $this->render('transaction.list.html.twig', $Data);
}
/**
......@@ -302,14 +361,15 @@ class TransactionController extends AbstractController
* @param $currentPage
* @return array
*/
private function paginationCalculator($txCount, $currentPage) {
private function paginationCalculator($txCount, $currentPage)
{
$lastPage = floor($txCount / self::TxPerPage);
$pageLinks = [];
for($i=$currentPage; $i >= 0 && $i >= $currentPage - 3 && $i <= $lastPage; $i--) {
for ($i = $currentPage; $i >= 0 && $i >= $currentPage - 3 && $i <= $lastPage; $i--) {
$pageLinks[] = $i;
}
for($i=$currentPage; $i >= $currentPage && $i <= $currentPage + 3 && $i <= $lastPage; $i++) {
for ($i = $currentPage; $i >= $currentPage && $i <= $currentPage + 3 && $i <= $lastPage; $i++) {
$pageLinks[] = $i;
}
......@@ -322,6 +382,4 @@ class TransactionController extends AbstractController
'pageLinks' => $pageLinks,
];
}
}
\ No newline at end of file
......@@ -19,6 +19,8 @@ class AppExtension extends AbstractExtension
new TwigFilter('toRSAddress', [$this, 'toRSAddress']),
new TwigFilter('str2bin', [$this, 'str2bin']),
new TwigFilter('toAccountId', [$this, 'toAccountId']),
new TwigFilter('sha256', [$this, 'sha256']),
new TwigFilter('planckToBurst', [$this, 'planckToBurst']),
];
}
......@@ -41,6 +43,14 @@ class AppExtension extends AbstractExtension
return Converter::toUnsinged($string);
}
public function sha256($string) {
return hash('sha256', $string);
}
public function planckToBurst($string) {
return number_format(round($string / 100000000, 2),2, '.', ',');
}
public function getEnv($string) {
if(isset($_ENV[$string])) {
return $_ENV[$string];
......
{% extends "common.layout.html.twig" %}
{% block main %}
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<p class="h3 text-center">Transaction #{{ transaction.id }}</p>
<table class="table table-striped table-hover table-sm">
<tr>
<th>Sender</th>
<td style="text-align: center">
<a href="{{ path('account_details', {id: transaction.sender_id | toAccountId }) }}">
{{ transaction.sender_id | toRSAddress }}<br />{{ transaction.sender_name }}
</a>
</td>
</tr>
<tr>
<th>Recipient{% if transaction.mo or transaction.mos %}s{% endif %}</th>
<td style="text-align: center">
{% if transaction.type == 0 and transaction.subtype != 0 %}
<table class="">
{% for details in transaction.mo_details %}
<td>
<td>
<a href="{{ path('account_details', {id: details.recipient_id | toAccountId }) }}">
{{ details.recipient_id | toRSAddress }}
</a>
</td>
<td style="word-wrap: break-word;min-width: 250px;max-width: 250px;">{{ details.recipient_name }}</td>
<td style="text-align: left">{{ details.amount | planckToBurst }} {{ getEnv('RS_PREFIX') }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<a href="{{ path('account_details', {id: transaction.recipient_id | toAccountId }) }}">
{{ transaction.recipient_id | toRSAddress }}<br />{{ transaction.recipient_name }}
</a>
{% endif %}
</td>
</tr>
<tr>
<th>{% if transaction.mo or transaction.mos %}Total {% endif %}Amount</th>
<td>{{ transaction.amount | planckToBurst }} {{ getEnv('RS_PREFIX') }}</td>
</tr>
<tr>
<th>Fee</th>
<td>{{ transaction.fee | planckToBurst }} {{ getEnv('RS_PREFIX') }}</td>
</tr>
<tr>
<th>Block</th>
<td><a href="{{ url('block_details', {'block_id': transaction.block_id}) }}">#{{ transaction.height }} @ {{ transaction.block_unixtime | date ("Y-m-d H:i:s", "Europe/Berlin") }}</a></td>
</tr>
<tr>
<th>Type</th>
<td>{{ transaction.type_name }}</td>
</tr>
<tr>
<th>Timestamp</th>
<td>{{ transaction.unixtime | date ("Y-m-d H:i:s", "Europe/Berlin") }}</td>
</tr>
<tr>
<th>Signature</th>
<td style="word-wrap: break-word;min-width: 160px;max-width: 160px;">{{ transaction.signature | str2bin }}</td>
</tr>
<tr>
<th>Full Hash</th>
<td style="word-wrap: break-word;min-width: 160px;max-width: 160px;">{{ transaction.full_hash | str2bin }}</td>
</tr>
</table>
</div>
</div>
{% endblock %}
\ No newline at end of file
......@@ -54,7 +54,7 @@
<tbody>
{% for transaction in transactions %}
<tr>
<td><a href="#">{{ transaction.id }}</a></td>
<td><a href="{{ path('transaction_details', {'id': transaction.id}) }}">{{ transaction.id }}</a></td>
<td>{{ transaction.transaction_type }}</td>
<td style="text-align: center">
<a href="{{ path('account_details', {id: transaction.sender_id | toAccountId }) }}">
......@@ -67,7 +67,7 @@
{{ transaction.recipient_id | toRSAddress }}<br />{{ transaction.recipient_name }}
</a>
{% else %}
Multiple Recipients, <a href="#">Details</a>
Multiple Recipients, <a href="{{ path('transaction_details', {'id': transaction.id}) }}">Details</a>
{% endif %}
</td>
<td>{{ transaction.amount }}</td>
......