offlineblockexplorer.py


SUBMITTED BY: Guest

DATE: Oct. 14, 2012, 5:17 a.m.

FORMAT: Python

SIZE: 14.6 kB

HITS: 2213

  1. # Python 2.5
  2. # terms of use: public domain. you are free to use or abuse this code anyway you want :)
  3. import socket, base64, hashlib
  4. op={
  5. chr(0): 'OP_FALSE', # this value is replaced later
  6. chr(81): 'OP_TRUE', # this value is replaced later
  7. chr(0): 'OP_0',
  8. chr(76): 'OP_PUSHDATA1',
  9. chr(77): 'OP_PUSHDATA2',
  10. chr(78): 'OP_PUSHDATA4',
  11. chr(79): 'OP_1NEGATE',
  12. chr(81): 'OP_1',
  13. chr(97): 'OP_NOP',
  14. chr(99): 'OP_IF',
  15. chr(100): 'OP_NOTIF',
  16. chr(103): 'OP_ELSE',
  17. chr(104): 'OP_ENDIF',
  18. chr(105): 'OP_VERIFY',
  19. chr(106): 'OP_RETURN',
  20. chr(107): 'OP_TOALTSTACK',
  21. chr(108): 'OP_FROMALTSTACK',
  22. chr(115): 'OP_IFDUP',
  23. chr(116): 'OP_DEPTH',
  24. chr(117): 'OP_DROP',
  25. chr(118): 'OP_DUP',
  26. chr(119): 'OP_NIP',
  27. chr(120): 'OP_OVER',
  28. chr(121): 'OP_PICK',
  29. chr(122): 'OP_ROLL',
  30. chr(123): 'OP_ROT',
  31. chr(124): 'OP_SWAP',
  32. chr(125): 'OP_TUCK',
  33. chr(109): 'OP_2DROP',
  34. chr(110): 'OP_2DUP',
  35. chr(111): 'OP_3DUP',
  36. chr(112): 'OP_2OVER',
  37. chr(113): 'OP_2ROT',
  38. chr(114): 'OP_2SWAP',
  39. chr(126): 'OP_CAT',
  40. chr(127): 'OP_SUBSTR',
  41. chr(128): 'OP_LEFT',
  42. chr(129): 'OP_RIGHT',
  43. chr(130): 'OP_SIZE',
  44. chr(131): 'OP_INVERT',
  45. chr(132): 'OP_AND',
  46. chr(133): 'OP_OR',
  47. chr(134): 'OP_XOR',
  48. chr(135): 'OP_EQUAL',
  49. chr(136): 'OP_EQUALVERIFY',
  50. chr(139): 'OP_1ADD',
  51. chr(140): 'OP_1SUB',
  52. chr(141): 'OP_2MUL',
  53. chr(142): 'OP_2DIV',
  54. chr(143): 'OP_NEGATE',
  55. chr(144): 'OP_ABS',
  56. chr(145): 'OP_NOT',
  57. chr(146): 'OP_0NOTEQUAL',
  58. chr(147): 'OP_ADD',
  59. chr(148): 'OP_SUB',
  60. chr(149): 'OP_MUL',
  61. chr(150): 'OP_DIV',
  62. chr(151): 'OP_MOD',
  63. chr(152): 'OP_LSHIFT',
  64. chr(153): 'OP_RSHIFT',
  65. chr(154): 'OP_BOOLAND',
  66. chr(155): 'OP_BOOLOR',
  67. chr(156): 'OP_NUMEQUAL',
  68. chr(157): 'OP_NUMEQUALVERIFY',
  69. chr(158): 'OP_NUMNOTEQUAL',
  70. chr(159): 'OP_LESSTHAN',
  71. chr(160): 'OP_GREATERTHAN',
  72. chr(161): 'OP_LESSTHANOREQUAL',
  73. chr(162): 'OP_GREATERTHANOREQUAL',
  74. chr(163): 'OP_MIN',
  75. chr(164): 'OP_MAX',
  76. chr(165): 'OP_WITHIN',
  77. chr(166): 'OP_RIPEMD160',
  78. chr(167): 'OP_SHA1',
  79. chr(168): 'OP_SHA256',
  80. chr(169): 'OP_HASH160',
  81. chr(170): 'OP_HASH256',
  82. chr(171): 'OP_CODESEPARATOR',
  83. chr(172): 'OP_CHECKSIG',
  84. chr(173): 'OP_CHECKSIGVERIFY',
  85. chr(174): 'OP_CHECKMULTISIG',
  86. chr(175): 'OP_CHECKMULTISIGVERIFY',
  87. chr(253): 'OP_PUBKEYHASH',
  88. chr(254): 'OP_PUBKEY',
  89. chr(255): 'OP_INVALIDOPCODE',
  90. chr(80): 'OP_RESERVED',
  91. chr(98): 'OP_VER',
  92. chr(101): 'OP_VERIF',
  93. chr(102): 'OP_VERNOTIF',
  94. chr(137): 'OP_RESERVED1',
  95. chr(138): 'OP_RESERVED2'
  96. }
  97. for i in range(1,76): op[chr(i)]='OP_PUSH'+str(i) # name isn't shown in the script
  98. for i in range(82,97): op[chr(i)]='OP_'+str(i-80) # name isn't shown in the script
  99. for i in range(176,186): op[chr(i)]='OP_NOP'+str(i-175)
  100. for i in range(256): op[chr(i)]= op[chr(i)] if (chr(i) in op) else 'OP_UNKNOWN'
  101. class jsonrpcserver():
  102. def __init__(self, host, port, username, password):
  103. self.jsonsock=socket.socket()
  104. self.jsonsock.connect((host,port))
  105. self.authkey=base64.b64encode(username+':'+password)
  106. class jsonerror(Exception):
  107. def __init__(self, errormessage, response):
  108. self.errormessage = errormessage
  109. self.response = response
  110. def __str__(self):
  111. return str(self.errormessage)
  112. def request(self, method, params, errormessage):
  113. k='POST / HTTP/1.1\r\nContent-Type: application/json-rpc\r\nAuthorization: Basic '+self.authkey+'\r\nContent-Length: '
  114. k2='{"method": "'+method+'", "params": '+str(params).replace("'",'"')+', "id":0}'
  115. self.jsonsock.send(k+str(len(k2))+'\r\n\r\n'+k2); response=self.jsonsock.recv(2**20)
  116. while response.count('{')!=response.count('}'): response+=self.jsonsock.recv(2**20)
  117. c=eval(response.split('\r\n\r\n')[-1].replace('null','None'))
  118. if c['error']:
  119. raise self.jsonerror(errormessage+' - "'+str(c['error']['message'])+'"', c)
  120. return c['result']
  121. def getrawtx(self, hash):
  122. return self.request('getrawtransaction', [hash], 'getrawtx failed to retrieve tx '+hash)
  123. def getblock(self, hash):
  124. return self.request('getblock', [hash], 'getblock failed to retrieve block '+hash)
  125. def getblockcount(self):
  126. return self.request('getblockcount', [], 'getblockcount failed to retrieve block count')
  127. def getblockhash(self, blocknum):
  128. return self.request('getblockhash', [blocknum], 'getblockhash failed to retrieve hash for block #'+str(blocknum))
  129. #bitcoinqt=jsonrpcserver('localhost',8332,'bitcoinrpc','password')
  130. #print bitcoinqt.getrawtx('384287be164a79c707f93a687a57ec5d9c1230af521a546186cd5c3af2011dae')
  131. #print bitcoinqt.getblock('00000000000000eac823d5c3f6c17f9ec08414c4e23fff6060e842fae0581fd6')
  132. #print bitcoinqt.getblockcount()
  133. #print bitcoinqt.getblockhash(0)
  134. def hash256(x): return hashlib.sha256(hashlib.sha256(x).digest()).digest()
  135. def makemerkletree(txhashes):
  136. levels=[txhashes]
  137. while len(levels[-1])!=1:
  138. newlevel=[]
  139. for l in range((len(levels[-1])+1)/2):
  140. newlevel.append(hash256(levels[-1][l*2].decode('hex')[::-1]+levels[-1][min(l*2+1,len(levels[-1])-1)].decode('hex')[::-1])[::-1].encode('hex'))
  141. levels.append(newlevel)
  142. tree=[]
  143. for level in levels: tree+=level
  144. return tree
  145. def humanreadablescript(script): # same as blockexplorer/getblock's, I personally prefer OP_FALSE and OP_TRUE
  146. out='' # maybe this function should be called asm ? what does asm stand for ?
  147. while script!='':
  148. if 'OP_PUSH' in op[script[0]]:
  149. if 'OP_PUSHDATA' in op[script[0]]: # 199593
  150. numbytes=int(op[script[0]][-1]); script=script[1:]
  151. pushlen=sum([ord(script[x])*(256**x) for x in range(numbytes)]); script=script[numbytes:]
  152. else: pushlen=ord(script[0]); script=script[1:]
  153. out+=script[:pushlen].encode('hex')+' '; script=script[pushlen:]
  154. elif op[script[0]][3:].isdigit(): out+=op[script[0]][3:]+' '; script=script[1:] # 199388 # 201121
  155. else:
  156. out+=op[script[0]]+' '; script=script[1:]
  157. return out.strip()
  158. #print humanreadablescript('76a914de7a247c0d617aafa5823994ecb648411e008f1288ac'.decode('hex'))
  159. def humanreadabletx(rawtx): # similar to decoderawtransaction, but faster than using the rpc. also, fun to write
  160. tx={} # needs better name # code could be prettier
  161. tx['size']=len(rawtx)
  162. tx['hash']=hash256(rawtx)[::-1].encode('hex')
  163. tx['ver']=sum([ord(rawtx[x])*(256**x) for x in range(4)]); rawtx=rawtx[4:]
  164. numinputs=ord(rawtx[0]); rawtx=rawtx[1:]
  165. if numinputs==253: numinputs=sum([ord(rawtx[x])*(256**x) for x in range(2)]); rawtx=rawtx[2:] # 199090
  166. tx['vin_sz']=numinputs
  167. tx['in']=[]
  168. for inputnum in range(numinputs):
  169. input={'prev_out':{}}
  170. input['prev_out']['hash']=rawtx[:32][::-1].encode('hex'); rawtx=rawtx[32:]
  171. if input['prev_out']['hash']=='0'*64:
  172. input['prev_out']['n']=sum([ord(rawtx[x])*(256**x) for x in range(4)]); rawtx=rawtx[4:]
  173. coinbasesize=ord(rawtx[0]); rawtx=rawtx[1:]
  174. if coinbasesize==253: coinbasesize=sum([ord(rawtx[x])*(256**x) for x in range(2)]); rawtx=rawtx[2:] # 199593
  175. input['coinbase']=rawtx[:coinbasesize].encode('hex'); rawtx=rawtx[coinbasesize:]
  176. input['sequence']=sum([ord(rawtx[x])*(256**x) for x in range(4)]); rawtx=rawtx[4:] # 199002
  177. if input['sequence']==256**4-1: del input['sequence']
  178. else:
  179. input['prev_out']['n']=sum([ord(rawtx[x])*(256**x) for x in range(4)]); rawtx=rawtx[4:]
  180. sigsize=ord(rawtx[0]); rawtx=rawtx[1:]
  181. if sigsize==253: sigsize=sum([ord(rawtx[x])*(256**x) for x in range(2)]); rawtx=rawtx[2:] # 199593
  182. input['scriptSig']=humanreadablescript(rawtx[:sigsize]); rawtx=rawtx[sigsize:]
  183. input['sequence']=sum([ord(rawtx[x])*(256**x) for x in range(4)]); rawtx=rawtx[4:]
  184. if input['sequence']==256**4-1: del input['sequence']
  185. tx['in'].append(input)
  186. numoutputs=ord(rawtx[0]); rawtx=rawtx[1:]
  187. if numoutputs==253: numoutputs=sum([ord(rawtx[x])*(256**x) for x in range(2)]); rawtx=rawtx[2:] # 199025
  188. tx['vout_sz']=numoutputs
  189. tx['out']=[]
  190. for outputnum in range(numoutputs):
  191. output={}
  192. numericvalue=sum([ord(rawtx[x])*(256**x) for x in range(8)]); rawtx=rawtx[8:]
  193. output['value']=str(numericvalue/100000000)+'.'+str(numericvalue%100000000).zfill(8)
  194. scriptsize=ord(rawtx[0]); rawtx=rawtx[1:]
  195. if scriptsize==253: scriptsize=sum([ord(rawtx[x])*(256**x) for x in range(2)]); rawtx=rawtx[2:] # 71036
  196. output['scriptPubKey']=humanreadablescript(rawtx[:scriptsize]); rawtx=rawtx[scriptsize:]
  197. tx['out'].append(output)
  198. tx['lock_time']=sum([ord(rawtx[x])*(256**x) for x in range(4)]); rawtx=rawtx[4:]
  199. return tx
  200. #print humanreadabletx('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000'.decode('hex')) # genesis transaction, good luck trying to get that from bitcoinqt though. I wonder if Satoshi can spend that tx if he wanted to...
  201. # grunt work
  202. def bxformattx(hrtx): # block explorer format
  203. out ='{\n'
  204. out+=' "hash":"'+hrtx['hash']+'",\n'
  205. out+=' "ver":'+str(hrtx['ver'])+',\n'
  206. out+=' "vin_sz":'+str(hrtx['vin_sz'])+',\n'
  207. out+=' "vout_sz":'+str(hrtx['vout_sz'])+',\n'
  208. out+=' "lock_time":'+str(hrtx['lock_time'])+',\n'
  209. out+=' "size":'+str(hrtx['size'])+',\n'
  210. out+=' "in":[\n'
  211. for input in hrtx['in']:
  212. out+=' {\n'
  213. out+=' "prev_out":{\n'
  214. out+=' "hash":"'+input['prev_out']['hash']+'",\n'
  215. out+=' "n":'+str(input['prev_out']['n'])+'\n'
  216. out+=' },\n'
  217. field=['scriptSig','coinbase'][input['prev_out']['hash']=='0'*64]
  218. out+=' "'+field+'":"'+input[field]+'"\n'
  219. if 'sequence' in input:
  220. out=out[:-1]+',\n'
  221. out+=' "sequence":'+str(input['sequence'])+'\n'
  222. out+=' },\n'
  223. out=out[:-2]+'\n' # removing the last comma
  224. out+=' ],\n'
  225. out+=' "out":[\n'
  226. for output in hrtx['out']:
  227. out+=' {\n'
  228. out+=' "value":"'+output['value']+'",\n'
  229. out+=' "scriptPubKey":"'+output['scriptPubKey']+'"\n'
  230. out+=' },\n'
  231. out=out[:-2]+'\n' # removing the last comma
  232. out+=' ]\n'
  233. out+='}'
  234. return out
  235. def bxformatblock(block, transactiondata): # block explorer format
  236. out ='{\n'
  237. out+=' "hash":"'+block['hash']+'",\n'
  238. out+=' "ver":'+str(block['version'])+',\n'
  239. out+=' "prev_block":"'+block['previousblockhash']+'",\n'
  240. out+=' "mrkl_root":"'+block['merkleroot']+'",\n'
  241. out+=' "time":'+str(block['time'])+',\n'
  242. out+=' "bits":'+str(int(block['bits'],16))+',\n'
  243. out+=' "nonce":'+str(block['nonce'])+',\n'
  244. out+=' "n_tx":'+str(len(block['tx']))+',\n'
  245. out+=' "size":'+str(block['size'])+',\n'
  246. out+=' "tx":[\n'
  247. for transactionhash in block['tx']:
  248. out+=' '+bxformattx(transactiondata[transactionhash]).replace('\n','\n ')+',\n' # add indentation
  249. out=out[:-2]+'\n' # removing the last comma
  250. out+=' ],\n'
  251. out+=' "mrkl_tree":[\n'
  252. for i in makemerkletree(block['tx']):
  253. out+=' "'+i+'",\n'
  254. out=out[:-2]+'\n' # removing the last comma
  255. out+=' ]\n'
  256. out+='}'
  257. return out
  258. # end of grunt work
  259. def bxgettx(txid, server):
  260. return bxformattx(humanreadabletx(server.getrawtx(txid).decode('hex')))
  261. def bxgetblock(blockhash, server):
  262. blockdata=server.getblock(blockhash)
  263. txdata={}
  264. for tx in blockdata['tx']: txdata[tx]=humanreadabletx(server.getrawtx(tx).decode('hex'))
  265. return bxformatblock(blockdata,txdata)
  266. #bitcoinqt=jsonrpcserver('localhost',8332,'bitcoinrpc','password')
  267. #print bxgetblock('00000000000000eac823d5c3f6c17f9ec08414c4e23fff6060e842fae0581fd6', bitcoinqt)
  268. # http server for offline block explorer
  269. import threading
  270. sock=socket.socket()
  271. sock.bind(('',8334))
  272. sock.listen(100)
  273. def handleconnection(newconnection):
  274. bitcoinqt=jsonrpcserver('localhost',8332,'bitcoinrpc','password')
  275. req=newconnection.recv(2**20)
  276. reqpage=req.split('\r\n')[0].split(' ')[1]
  277. foundheader='HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: '
  278. notfoundheader='HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: '
  279. def response(data, found=True): return [notfoundheader,foundheader][found]+str(len(data))+'\r\n\r\n'+data
  280. try:
  281. if reqpage[:10]=='/rawblock/': resptext=response(bxgetblock(reqpage[10:], bitcoinqt))
  282. elif reqpage[:7]=='/rawtx/': resptext=response(bxgettx(reqpage[7:], bitcoinqt))
  283. elif reqpage=='/q/getblockcount': resptext=response(str(bitcoinqt.getblockcount()))
  284. elif reqpage[:16]=='/q/getblockhash/': resptext=response(bitcoinqt.getblockhash(int(reqpage[16:])).upper())
  285. else: resptext=response('Nothing here.', found=False)
  286. except jsonrpcserver.jsonerror, e: resptext=response(str(e.response))
  287. except: resptext=response('Nothing here.', found=False); print 'ERROR - disable line 324 and restart to see what went wrong'
  288. newconnection.send(resptext); newconnection.close()
  289. while True: threading.Thread(target=handleconnection, args=(sock.accept()[0],)).start()

comments powered by Disqus