script/research/blockchain-explorer/site: rest pages added, cleaned up code, licencing added

This commit is contained in:
skoupidi
2024-06-18 14:19:33 +03:00
parent cfd1e65c39
commit 0890ebf458
12 changed files with 351 additions and 50 deletions

View File

@@ -18,7 +18,8 @@ First we start a `darkfid` localnet:
It is advised to shutdown the `minerd` daemon after couple of blocks, to not waste resources.
Now we start a `blockchain-explorer` daemon:
Update the `blockchain-explorer` configuration to the localnet `darkfid` JSON-RPC endpoint
and start a the daemon:
```
% cd script/research/blockchain-explorer

View File

@@ -1,17 +1,59 @@
from flask import Flask, render_template
# This file is part of DarkFi (https://dark.fi)
#
# Copyright (C) 2020-2024 Dyne.org foundation
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import asyncio
from flask import Flask, request, render_template
import rpc
# DarkFi blockchain-explorer daemon JSON-RPC configuration
# TODO: make this configurable
URL = "127.0.0.1"
PORT = 14567
app = Flask(__name__)
@app.route('/')
async def index():
blocks = await rpc.get_last_n_blocks(10, URL, PORT)
stats = await rpc.get_basic_statistics(URL, PORT)
blocks = await rpc.get_last_n_blocks("10")
stats = await rpc.get_basic_statistics()
return render_template('index.html', blocks=blocks, stats=stats)
@app.route('/search', methods=['GET', 'POST'])
async def search():
search_hash = request.args.get('search_hash', '')
try:
block = await rpc.get_block(search_hash)
transactions = await rpc.get_block_transactions(search_hash)
return render_template('block.html', block=block, transactions=transactions)
except Exception:
transaction = await rpc.get_transaction(search_hash)
return render_template('transaction.html', transaction=transaction)
@app.route('/block/<header_hash>')
async def block(header_hash):
block = await rpc.get_block(header_hash)
transactions = await rpc.get_block_transactions(header_hash)
return render_template('block.html', block=block, transactions=transactions)
@app.route('/transaction/<transaction_hash>')
async def transaction(transaction_hash):
transaction = await rpc.get_transaction(transaction_hash)
return render_template('transaction.html', transaction=transaction)
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@app.errorhandler(500)
def page_not_found(e):
return render_template('500.html'), 500

View File

@@ -1,8 +1,28 @@
# This file is part of DarkFi (https://dark.fi)
#
# Copyright (C) 2020-2024 Dyne.org foundation
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import asyncio, json, random
from flask import abort
# TODO have a single channel for the whole app
# Class representing the channel with the JSON-RPC server.
# DarkFi blockchain-explorer daemon JSON-RPC configuration
URL = "127.0.0.1"
PORT = 14567
# Class representing the channel with the JSON-RPC server
class Channel:
def __init__(self, reader, writer):
self.reader = reader
@@ -40,10 +60,9 @@ async def create_channel(server_name, port):
channel = Channel(reader, writer)
return channel
# Execute a request towards the JSON-RPC server
async def query(method, params, server_name, port):
channel = await create_channel(server_name, port)
async def query(method, params):
channel = await create_channel(URL, PORT)
request = {
"id": random.randint(0, 2**32),
"method": method,
@@ -55,21 +74,34 @@ async def query(method, params, server_name, port):
response = await channel.receive()
# Closed connect returns None
if response is None:
print("error: connection with server was closed", file=sys.stderr)
print("error: connection with server was closed")
abort(500)
# Erroneous query is handled with not found
if "error" in response:
error = response["error"]
errcode, errmsg = error["code"], error["message"]
print(f"error: {errcode} - {errmsg}", file=sys.stderr)
abort(500)
print(f"error: {errcode} - {errmsg}")
abort(404)
return response["result"]
# Retrieve last n blocks from blockchain-explorer daemon
async def get_last_n_blocks(n, server_name, port):
return await query("blocks.get_last_n_blocks", [str(n)], server_name, int(port))
async def get_last_n_blocks(n: str):
return await query("blocks.get_last_n_blocks", [n])
# Retrieve basic statistics from blockchain-explorer daemon
async def get_basic_statistics(server_name, port):
return await query("statistics.get_basic_statistics", [], server_name, int(port))
async def get_basic_statistics():
return await query("statistics.get_basic_statistics", [])
# Retrieve the block information of given header hash from blockchain-explorer daemon
async def get_block(header_hash: str):
return await query("blocks.get_block_by_hash", [header_hash])
# Retrieve the transactions of given block header hash from blockchain-explorer daemon
async def get_block_transactions(header_hash: str):
return await query("transactions.get_transactions_by_header_hash", [header_hash])
# Retrieve the transaction information of given hash from blockchain-explorer daemon
async def get_transaction(transaction_hash: str):
return await query("transactions.get_transaction_by_hash", [transaction_hash])

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

View File

@@ -828,3 +828,17 @@ h5 a:hover{
display: none;
}
/* Container holding an image with text on top */
.background-container {
position: relative;
text-align: center;
color: white;
}
/* Top centered text inside a background-container */
.top-centered {
position: absolute;
top: 25%;
left: 50%;
transform: translate(-50%, -25%);
}

View File

@@ -0,0 +1,35 @@
<!--
This file is part of DarkFi (https://dark.fi)
Copyright (C) 2020-2024 Dyne.org foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
{% extends "base.html" %}
{% block title %}Not found!{% endblock %}
{% block content %}
<div class="background-container">
<img src="../static/img/darkfi25.jpg" style="width:100%;">
<div class="top-centered">
<h1 class="display-4 text-white border-l3" style="font-family:'Weissrundgotisch';line-height:.9em;">
Is this what you are looking for?
</h1>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,35 @@
<!--
This file is part of DarkFi (https://dark.fi)
Copyright (C) 2020-2024 Dyne.org foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
{% extends "base.html" %}
{% block title %}Not found!{% endblock %}
{% block content %}
<div class="background-container">
<img src="../static/img/darkfi05.jpg" style="width:100%;">
<div class="top-centered">
<h1 class="display-4 text-white border-l3" style="font-family:'Weissrundgotisch';line-height:.9em;">
Reality fractured beyond repair!
</h1>
</div>
</div>
{% endblock %}

View File

@@ -1,3 +1,22 @@
<!--
This file is part of DarkFi (https://dark.fi)
Copyright (C) 2020-2024 Dyne.org foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<!DOCTYPE html>
<html lang="en">

View File

@@ -0,0 +1,55 @@
<!--
This file is part of DarkFi (https://dark.fi)
Copyright (C) 2020-2024 Dyne.org foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
{% extends "base.html" %}
{% block title %}Block {{ block[0] }}{% endblock %}
{% block content %}
<div class="container-fluid pl-5 pb-5 pr-5 title-block" style="margin-top:90px;">
<h1 class="display-4 text-white border-l3" style="font-family:'Weissrundgotisch';line-height:.9em;">Block {{ block[0] }}</h1>
</div>
<div class="row m-0 border-secondary border-top">
<div class="text-white mb-0 border-bottom border-secondary lead-text" style="width:100%;">
<ul>
<li>Version: {{ block[1] }}</li>
<li>Previous: <a href="../block/{{ block[2] }}">{{ block[2] }}</a></li>
<li>Height: {{ block[3] }}</li>
<li>Timestamp: {{ block[4] }}</li>
<li>Nonce: {{ block[5] }}</li>
<li>Root: {{ block[6] }}</li>
<li>Signature: {{ block[7] }}</li>
</ul>
</div>
</div>
<div class="row m-0 border-secondary border-top">
<div class="text-white mb-0 border-bottom border-secondary lead-text" style="width:100%;">
<h2 class="display-8 text-white border-l3" style="font-family:'Weissrundgotisch';line-height:.9em;">Transactions</h2>
<ul>
{% for transaction in transactions %}
<li><a href="../transaction/{{ transaction[0] }}">{{ transaction[0] }}</a></li>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}

View File

@@ -1,38 +1,67 @@
<!--
This file is part of DarkFi (https://dark.fi)
Copyright (C) 2020-2024 Dyne.org foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
{% extends "base.html" %}
{% block title %}Explorer{% endblock %}
{% block content %}
<div class="container-fluid pl-5 pb-5 pr-5 title-block" style="margin-top:90px;">
<h1 class="display-4 text-white border-l3" style="font-family:'Weissrundgotisch';line-height:.9em;">Explorer</h1>
</div>
<div class="row m-0 border-secondary border-top"/>
<div class="col-md-7 text-white mb-0 border-right border-bottom border-secondary lead-text">
<h2 class="display-8 text-white border-l3" style="font-family:'Weissrundgotisch';line-height:.9em;">Latest Blocks</h2>
<table>
<tr style="text-align: center;">
<th>Height</th>
<th>Hash</th>
<th>timestamp</th>
</tr>
{% for block in blocks %}
<tr>
<td>{{ block[3] }}</td>
<td>{{ block[0] }}</td>
<td>{{ block[4] }}</td>
</tr>
{% endfor %}
</table>
<div class="container-fluid pl-5 pb-5 pr-5 title-block" style="margin-top:90px;">
<h1 class="display-4 text-white border-l3" style="font-family:'Weissrundgotisch';line-height:.9em;">Explore</h1>
<div class="search-container">
<form action="/search">
<input type="text" placeholder="Search for a block or transaction.." name="search_hash">
</form>
</div>
</div>
<div class="col-md-5 text-white m-0 border-secondary border-bottom lead-text">
<h2 class="display-8 text-white border-l3" style="font-family:'Weissrundgotisch';line-height:.9em;">Statistics</h2>
<ul>
<li>Height: {{ stats[0] }}</li>
<li>Epoch: {{ stats[1] }}</li>
<li>Last block: {{ stats[2] }}</li>
<li>Total blocks: {{ stats[3] }}</li>
<li>Total transactions: {{ stats[4] }}</li>
</ul>
<div class="row m-0 border-secondary border-top">
<div class="text-white mb-0 border-bottom border-secondary lead-text" style="width:100%;">
<h2 class="display-8 text-white border-l3" style="font-family:'Weissrundgotisch';line-height:.9em;">Latest Blocks</h2>
<table style="width:100%;">
<tr>
<th>Height</th>
<th>Hash</th>
<th>Timestamp</th>
</tr>
{% for block in blocks %}
<tr>
<td>{{ block[3] }}</td>
<td><a href="block/{{ block[0] }}">{{ block[0] }}</a></td>
<td>{{ block[4] }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<div class="row m-0 border-secondary border-top">
<div class="text-white mb-0 border-bottom border-secondary lead-text" style="width:100%;">
<h2 class="display-8 text-white border-l3" style="font-family:'Weissrundgotisch';line-height:.9em;">Statistics</h2>
<ul>
<li>Height: {{ stats[0] }}</li>
<li>Epoch: {{ stats[1] }}</li>
<li>Last block: <a href="block/{{ stats[2] }}">{{ stats[2] }}</a></li>
<li>Total blocks: {{ stats[3] }}</li>
<li>Total transactions: {{ stats[4] }}</li>
</ul>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,39 @@
<!--
This file is part of DarkFi (https://dark.fi)
Copyright (C) 2020-2024 Dyne.org foundation
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
{% extends "base.html" %}
{% block title %}Transaction {{ transaction[0] }}{% endblock %}
{% block content %}
<div class="container-fluid pl-5 pb-5 pr-5 title-block" style="margin-top:90px;">
<h1 class="display-4 text-white border-l3" style="font-family:'Weissrundgotisch';line-height:.9em;">Transaction {{ transaction[0] }}</h1>
</div>
<div class="row m-0 border-secondary border-top">
<div class="text-white mb-0 border-bottom border-secondary lead-text" style="width:100%;">
<ul>
<li>Block: <a href="../block/{{ transaction[1] }}">{{ transaction[1] }}</a></li>
<li>Payload: {{ transaction[2] }}</li>
</ul>
</div>
</div>
{% endblock %}