Skip to content

Commit 6c5a5ef

Browse files
committed
built new service_bismuth_explorer()
1 parent dae88c7 commit 6c5a5ef

File tree

9 files changed

+618
-6
lines changed

9 files changed

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

0 commit comments

Comments
 (0)