Skip to content

Commit 810fabf

Browse files
committed
built new service_blockchain_explorer() and updated id_server.py with new HTML template
1 parent dae88c7 commit 810fabf

11 files changed

+773
-34
lines changed
+393
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,393 @@
1+
import sys
2+
import time
3+
import sqlite3
4+
import base64
5+
6+
#------------------------------------------------------------------------------
7+
8+
if __name__ == '__main__':
9+
import os.path as _p
10+
sys.path.insert(0, _p.abspath(_p.join(_p.dirname(_p.abspath(sys.argv[0])), '..')))
11+
src_dir_path = _p.dirname(_p.dirname(_p.dirname(_p.abspath(sys.argv[0]))))
12+
sys.path.insert(0, src_dir_path)
13+
sys.path.insert(0, _p.join(src_dir_path, 'bitdust_forks', 'Bismuth'))
14+
15+
#------------------------------------------------------------------------------
16+
17+
from twisted.internet import reactor
18+
from twisted.web import server, resource
19+
20+
#------------------------------------------------------------------------------
21+
22+
from bitdust.logs import lg
23+
24+
from bitdust.lib import strng
25+
26+
from bitdust.main import settings
27+
from bitdust.main import config
28+
29+
from bitdust.interface import web_html_template
30+
31+
from bitdust.blockchain import bismuth_node
32+
33+
#------------------------------------------------------------------------------
34+
35+
_Debug = True
36+
_DebugLevel = 10
37+
38+
#------------------------------------------------------------------------------
39+
40+
_DataDirPath = None
41+
_ExplorerHost = None
42+
_ExplorerPort = None
43+
_WebListener = None
44+
45+
#------------------------------------------------------------------------------
46+
47+
48+
def init():
49+
global _DataDirPath
50+
global _ExplorerHost
51+
global _ExplorerPort
52+
global _WebListener
53+
54+
_DataDirPath = settings.ServiceDir('bismuth_blockchain')
55+
_ExplorerHost = config.conf().getString('services/blockchain-explorer/host', '127.0.0.1')
56+
_ExplorerPort = config.conf().getInt('services/blockchain-explorer/web-port', 19080)
57+
58+
root = BlockchainRootPage()
59+
root.putChild(b'', BlockchainMainPage())
60+
try:
61+
_WebListener = reactor.listenTCP(_ExplorerPort, server.Site(root)) # @UndefinedVariable
62+
if _Debug:
63+
lg.out(_DebugLevel, ' have started web server at port %d hostname=%s' % (_ExplorerPort, strng.to_text(_ExplorerHost)))
64+
except:
65+
if _Debug:
66+
lg.err('exception while trying to listen port ' + str(self.web_port))
67+
lg.exc()
68+
if _Debug:
69+
lg.args(_DebugLevel, data_dir_path=_DataDirPath)
70+
return True
71+
72+
73+
def shutdown():
74+
global _WebListener
75+
if _Debug:
76+
lg.dbg(_DebugLevel, '')
77+
if _WebListener:
78+
_WebListener.stopListening()
79+
if _Debug:
80+
lg.out(_DebugLevel, ' stopped web listener')
81+
_WebListener = None
82+
return True
83+
84+
85+
#------------------------------------------------------------------------------
86+
87+
88+
def execute(cursor, query, param):
89+
while True:
90+
try:
91+
cursor.execute(query, param)
92+
break
93+
except Exception as e:
94+
print('Database query: {} {}'.format(cursor, query))
95+
print('Database retry reason: {}'.format(e))
96+
time.sleep(0.2)
97+
return cursor
98+
99+
100+
#------------------------------------------------------------------------------
101+
102+
103+
class BlockchainMainPage(resource.Resource):
104+
def render_GET(self, request):
105+
global _ExplorerHost
106+
107+
page_size = 500
108+
109+
page_num = request.args.get(b'page', [])
110+
if page_num:
111+
try:
112+
page_num = int(strng.to_text(page_num[0]))
113+
except:
114+
lg.exc()
115+
page_num = 0
116+
else:
117+
page_num = 0
118+
119+
page_max = -1
120+
121+
src = ''
122+
123+
conn = sqlite3.connect(bismuth_node.nod().ledger_path, timeout=60.0)
124+
c = conn.cursor()
125+
execute(c, 'SELECT * FROM transactions ORDER BY block_height DESC, timestamp DESC LIMIT ? OFFSET ?;', (
126+
page_size,
127+
page_size*page_num,
128+
))
129+
_all = c.fetchall()
130+
c.close()
131+
conn.close()
132+
conn = None
133+
c = None
134+
135+
view = []
136+
b = -1
137+
x_old = 'init'
138+
139+
for x in _all:
140+
if int(x[0]) == 1:
141+
page_max = page_num
142+
143+
if x[0] != x_old:
144+
color_cell = '#F8F8F8'
145+
view.append('<tr><td>&nbsp;</td></tr>') #block separator
146+
else:
147+
color_cell = 'white'
148+
149+
view.append('<tr bgcolor ={}>'.format(color_cell))
150+
151+
if x[0] != x_old:
152+
b = b + 1
153+
154+
if x_old != x[0]:
155+
view.append('<td>{}</td>'.format(x[0])) #block height
156+
else:
157+
view.append('<td>{}</td>'.format(x[0])) #block height
158+
159+
view.append('<td>{}'.format(time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(float(x[1])))))
160+
view.append('<td>{}&hellip;</td>'.format(x[2][:6])) #from
161+
view.append('<td>{}&hellip;</td>'.format(x[3][:6])) #to
162+
view.append('<td>{0:g}</td>'.format(float(x[4]))) #amount
163+
164+
if x_old != x[0]:
165+
view.append('<td>{}&hellip;</td>'.format(x[7][:6])) #block hash
166+
else:
167+
view.append('<td>&nbsp;</td>') #block hash
168+
169+
view.append('<td>{0:g}</td>'.format(float(x[8]))) #fee
170+
view.append('<td>{0:g}</td>'.format(float(x[9]))) #reward
171+
172+
view.append('<td>{}{}</td>'.format(x[10][:16], '&hellip;' if len(x[10]) > 16 else '')) #operation
173+
view.append('<td>{}{}</td>'.format(x[11][:24], '&hellip;' if len(x[11]) > 24 else '')) #openfield
174+
175+
view.append('<td><a href="/{}">{}&hellip;</a></td>'.format(
176+
strng.to_text(base64.b64encode(strng.to_bin(x[5]), altchars=b'-_')),
177+
x[5][:6],
178+
)) #TXID
179+
180+
view.append('</tr>')
181+
182+
x_old = x[0]
183+
184+
src += '<div id="ui-blockchain-explorer" class="section bg-light">'
185+
src += '<div class="container">\n'
186+
src += '<div class="ui-card">\n'
187+
src += '<div class="card-body">\n'
188+
189+
src += '<div class="row justify-content-center">\n'
190+
src += '<h1 align=center>BitDust blockchain explorer</h1>\n'
191+
src += '</div>\n'
192+
193+
src += '<div class="row justify-content-center">\n'
194+
src += '<ul class="pagination pagination-sm">\n'
195+
src += '<li class="page-item">\n'
196+
src += '<a class="page-link" href="/?page={}">\n'.format(max(0, page_num - 1))
197+
src += '<span aria-hidden="true">&laquo;</span>\n'
198+
src += '</a>\n'
199+
src += '</li>\n'
200+
src += '<li class="page-item">\n'
201+
src += '<a class="page-link" href="/?page={}">\n'.format(min(page_max, page_num + 1))
202+
src += '<span aria-hidden="true">&raquo;</span>\n'
203+
src += '</a>\n'
204+
src += '</li>\n'
205+
src += '</ul>\n'
206+
src += '</div>\n'
207+
208+
src += '<div class="row justify-content-center">\n'
209+
210+
src += '<style type="text/css">#blockchain-table td {padding: 0px 5px; font-size: 0.9em; font-family: monospace, monospace; }</style>'
211+
212+
src += '<table id="blockchain-table" class="table table-responsive">\n'
213+
214+
src += '<tr bgcolor=white>\n'
215+
src += '<td><b>Block</b></td>\n'
216+
src += '<td><b>Timestamp</b></td>\n'
217+
src += '<td><b>From</b></td>\n'
218+
src += '<td><b>To</b></td>\n'
219+
src += '<td><b>Amount</b></td>\n'
220+
src += '<td><b>Hash</b></td>\n'
221+
src += '<td><b>Fee</b></td>\n'
222+
src += '<td><b>Reward</b></td>\n'
223+
src += '<td><b>Operation</b></td>\n'
224+
src += '<td><b>Openfield</b></td>\n'
225+
src += '<td><b>TXID</b></td>\n'
226+
src += '</tr>\n'
227+
228+
src += ''.join(view)
229+
230+
src += '</table>\n'
231+
src += '</div>\n'
232+
233+
src += '<br>\n'
234+
src += '<div class="row justify-content-center">\n'
235+
src += '<ul class="pagination pagination-sm">\n'
236+
src += '<li class="page-item">\n'
237+
src += '<a class="page-link" href="/?page={}">\n'.format(max(0, page_num - 1))
238+
src += '<span aria-hidden="true">&laquo;</span>\n'
239+
src += '</a>\n'
240+
src += '</li>\n'
241+
src += '<li class="page-item">\n'
242+
src += '<a class="page-link" href="/?page={}">\n'.format(min(page_max, page_num + 1))
243+
src += '<span aria-hidden="true">&raquo;</span>\n'
244+
src += '</a>\n'
245+
src += '</li>\n'
246+
src += '</ul>\n'
247+
src += '</div>\n'
248+
249+
src += '</div>\n'
250+
src += '</div>\n'
251+
src += '</div>\n'
252+
src += '</div>\n'
253+
254+
html_src = web_html_template.WEB_ROOT_TEMPLATE % dict(
255+
title='BitDust blockchain explorer',
256+
site_url='https://bitdust.io',
257+
basepath='https://bitdust.io/',
258+
wikipath='https://bitdust.io/wiki/',
259+
idserverspath='https://identities.bitdust.io/',
260+
blockchainpath='https://blockchain.bitdust.io/',
261+
div_main_class='main blockchain',
262+
div_main_body=src,
263+
google_analytics='',
264+
)
265+
return strng.to_bin(html_src)
266+
267+
268+
#------------------------------------------------------------------------------
269+
270+
271+
class BlockchainTransactionPage(resource.Resource):
272+
def __init__(self, tx_id):
273+
resource.Resource.__init__(self)
274+
self.tx_id = tx_id
275+
276+
def render_GET(self, request):
277+
src = ''
278+
src += '<div id="ui-blockchain-transaction" class="section bg-light">'
279+
src += '<div class="container">\n'
280+
src += '<div class="ui-card">\n'
281+
src += '<div class="card-body">\n'
282+
283+
try:
284+
_t = strng.to_text(base64.b64decode(self.tx_id, altchars='-_'))
285+
except:
286+
src += '<p align=center>invalid transaction ID</p>\n'
287+
src += '</div>\n'
288+
src += '</div>\n'
289+
src += '</div>\n'
290+
src += '</div>\n'
291+
html_src = web_html_template.WEB_ROOT_TEMPLATE % dict(
292+
title='BitDust blockchain explorer',
293+
site_url='https://bitdust.io',
294+
basepath='https://bitdust.io/',
295+
wikipath='https://bitdust.io/wiki/',
296+
idserverspath='https://identities.bitdust.io/',
297+
blockchainpath='https://blockchain.bitdust.io/',
298+
div_main_class='main blockchain-transaction',
299+
div_main_body=src,
300+
google_analytics='',
301+
)
302+
return strng.to_bin(html_src)
303+
304+
try:
305+
conn = sqlite3.connect(bismuth_node.nod().ledger_path, timeout=60.0)
306+
c = conn.cursor()
307+
execute(c, 'SELECT * FROM transactions WHERE substr(signature,1,4)=substr(?1,1,4) and signature like ?1;', (_t + '%', ))
308+
raw = c.fetchone()
309+
c.close()
310+
conn.close()
311+
conn = None
312+
c = None
313+
except:
314+
src += '<p align=center>reading failed</p>\n'
315+
src += '</div>\n'
316+
src += '</div>\n'
317+
src += '</div>\n'
318+
src += '</div>\n'
319+
html_src = web_html_template.WEB_ROOT_TEMPLATE % dict(
320+
title='BitDust blockchain explorer',
321+
site_url='https://bitdust.io',
322+
basepath='https://bitdust.io/',
323+
wikipath='https://bitdust.io/wiki/',
324+
idserverspath='https://identities.bitdust.io/',
325+
blockchainpath='https://blockchain.bitdust.io/',
326+
div_main_class='main blockchain-transaction',
327+
div_main_body=src,
328+
google_analytics='',
329+
)
330+
return strng.to_bin(html_src)
331+
332+
src += '<div>block: <b>{}</b></div><br>\n'.format(raw[0])
333+
src += '<div>block hash: <b><code>{}</code></b></div><br>\n'.format(raw[7])
334+
src += '<div>timestamp: <b>{}</b></div><br>\n'.format(time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(float(raw[1]))))
335+
src += '<div>sender: <b><code>{}</code></b></div><br>\n'.format(raw[2])
336+
src += '<div>recipient: <b><code>{}</code></b></div><br>\n'.format(raw[3])
337+
src += '<div>amount: <b>{0:g}</b></div><br>\n'.format(raw[4])
338+
src += '<div>fee: <b>{0:g}</b></div><br>\n'.format(raw[8])
339+
src += '<div>reward: <b>{0:g}</b></div><br>\n'.format(raw[9])
340+
src += '<div>operation: <b>{}</b></div><br>\n'.format(raw[10])
341+
src += '<div style="margin: 0 auto; overflow-wrap: break-word; word-wrap: break-word;">openfield: <b><code>{}</code></b></div>\n'.format(raw[11])
342+
src += '<div style="margin: 0 auto; overflow-wrap: break-word; word-wrap: break-word;">signature:\n<b><code>{}</code></b></div><br>\n'.format(self.tx_id)
343+
src += '</div>\n'
344+
src += '</div>\n'
345+
src += '</div>\n'
346+
src += '</div>\n'
347+
348+
html_src = web_html_template.WEB_ROOT_TEMPLATE % dict(
349+
title='BitDust blockchain explorer',
350+
site_url='https://bitdust.io',
351+
basepath='https://bitdust.io/',
352+
wikipath='https://bitdust.io/wiki/',
353+
idserverspath='https://identities.bitdust.io/',
354+
blockchainpath='https://blockchain.bitdust.io/',
355+
div_main_class='main blockchain-transaction',
356+
div_main_body=src,
357+
google_analytics='',
358+
)
359+
return strng.to_bin(html_src)
360+
361+
362+
#------------------------------------------------------------------------------
363+
364+
365+
class BlockchainRootPage(resource.Resource):
366+
def getChild(self, path, request):
367+
if not path:
368+
return self
369+
try:
370+
path = strng.to_text(path)
371+
except:
372+
return resource.NoResource('Not found')
373+
if path:
374+
return BlockchainTransactionPage(path)
375+
return resource.NoResource('Not found')
376+
377+
378+
#------------------------------------------------------------------------------
379+
380+
381+
def main():
382+
bismuth_node.init()
383+
settings.init()
384+
reactor.addSystemEventTrigger('before', 'shutdown', shutdown) # @UndefinedVariable
385+
reactor.callWhenRunning(init) # @UndefinedVariable
386+
reactor.run() # @UndefinedVariable
387+
settings.shutdown()
388+
389+
390+
#------------------------------------------------------------------------------
391+
392+
if __name__ == '__main__':
393+
main()

0 commit comments

Comments
 (0)