Post
Topic
Board Armory
Re: Armory BlockDataManager BDM, (TheBDM)
by
Nikhil18
on 02/10/2016, 00:42:38 UTC
import sys
import shelve
import fileinput
import bsddb
# import ipdb
# from IPython.core.debugger import Tracer

from math import sqrt
from time import sleep
import datetime
from calendar import timegm

sys.path.append('.')
sys.path.append('bitcointools')
sys.path.append('BitcoinArmory')
print sys.path


from armoryengine import *
from armoryengine.BDM import BDM_BLOCKCHAIN_READY, TheBDM
from bitcointools.deserialize import extract_public_key
from bitcointools.util import short_hex, long_hex


# Run with:
# PYTHONPATH=$PYTHONPATH:/home//bitcoin/armory/; python armory_block_reader.py --satoshi-datadir ~/.bitcoin_backup


def get_block_datetime(timestamp):
   # block_datetime = datetime.utcfromtimestamp(timestamp)
   block_datetime = datetime.utcfromtimestamp(timestamp)
   return "%d-%02d-%02d-%02d-%02d-%02d"%(block_datetime.year, block_datetime.month,
                                       block_datetime.day, block_datetime.hour,
                                       block_datetime.minute, block_datetime.second)

class BlockReader:
   def __init__(self):
      self.index_db = shelve.open('armory_index');     
      self.bdm = TheBDM
      self.verbose = False
      self.coinbase = '0000000000000000000000000000000000000000000000000000000000000000'
      # self.pk_dict = {}
      self.pk_dict = bsddb.hashopen('pk_dict.bdb', 'n', cachesize=2000000)

   def cursory_check(self, t, lo=None, hi=None):
      if lo is None or hi is None:
         return False

      loblk = self.bdm.getHeaderByHeight(lo)
      lopblk = PyBlockHeader().fromCpp(loblk) 

      hiblk = self.bdm.getHeaderByHeight(hi)
      hipblk = PyBlockHeader().fromCpp(hiblk) 

      if self.verbose:
         print 'Block: %i, @ %s' % (lo, get_block_datetime(lopblk.timestamp))   
         print 'Block: %i, @ %s' % (hi, get_block_datetime(hipblk.timestamp))   

      return lopblk.timestamp < t and hipblk.timestamp > t

   def binary_search(self, t, lo, hi):
      pos = int((lo + hi) / 2)
      blk = self.bdm.getHeaderByHeight(pos)
      pblk = PyBlockHeader().fromCpp(blk) 

      # Write to db
      if not self.index_db.has_key(str(pos)):
         self.index_db[str(pos)] = get_block_datetime(pblk.timestamp)
      if self.verbose:
         print 'Lo: %i, Hi: %i, Pos: %i(%s)' % (lo, hi, pos, get_block_datetime(pblk.timestamp))

      if (hi - lo) < 2:
         return self.binary_search(t, lo-1, lo+1)
      if (hi - lo) == 2:
         return pos
      if pblk.timestamp < t:
         return self.binary_search(t, pos, hi)
      elif pblk.timestamp > t:
         return self.binary_search(t, lo, pos)

   def load_block_chain(self):
      start = datetime.datetime.now()
      self.bdm.setBlocking(True)
      self.bdm.setOnlineMode(True)

      # The setOnlineMode should block until blockchain loading is complete
      print 'Loading blockchain took %0.1f sec' % (datetime.datetime.now() - start)

      # Indexing
      print 'Indexing ...'
      if not self.index_db.has_key('top_block_height'):
         self.index_db['top_block_height'] = self.bdm.getTopBlockHeight()

      print 'Top Block Height: ', self.index_db['top_block_height']
      # t1 = timegm(datetime(2011,01,01,0,0,0).timetuple())
      # t2 = timegm(datetime(2011,01,02,0,0,0).timetuple())

      # silkroad-arrestg
      t1 = timegm(datetime(2013,3,25,18,0,0).timetuple())
      t2 = timegm(datetime(2013,10,25,18,0,0).timetuple())

      # # silkroad-arrest
      # t1 = timegm(datetime(2013,10,23,0,0,0).timetuple())
      # t2 = timegm(datetime(2013,10,26,0,0,0).timetuple())

      print '------------------------------------------'
      t1_check = self.cursory_check(t1, lo=0, hi=self.bdm.getTopBlockHeight())
      t2_check = self.cursory_check(t2, lo=0, hi=self.bdm.getTopBlockHeight())
      print 'Cursory Check t1 %s: %s' % (get_block_datetime(t1), t1_check)
      print 'Cursory Check t2 %s: %s' % (get_block_datetime(t2), t2_check)

      print '------------------------------------------'
      t1_pos = self.binary_search(t1, lo=0, hi=self.bdm.getTopBlockHeight())
      # t1_pos = 0;
      t2_pos = self.binary_search(t2, lo=0, hi=self.bdm.getTopBlockHeight())
      print 'Binary Search t1 %s: %s' % (get_block_datetime(t1), t1_pos)
      print 'Binary Search t2 %s: %s' % (get_block_datetime(t2), t2_pos)

      print '- Range [%i, %i] --------------------------' % (t1_pos, t2_pos+1)
      return range(t1_pos, t2_pos+1)

      # pblk = PyBlockHeader().fromCpp(TheBDM.getTopBlockHeader()) 
      # block_datetime = datetime.utcfromtimestamp(pblk.timestamp)
      # dt = "%d-%02d-%02d-%02d-%02d-%02d"%(block_datetime.year, block_datetime.month,
      #                                     block_datetime.day, block_datetime.hour,
      #                                     block_datetime.minute, block_datetime.second)
      # print 'Block: %s, @ %s\n' % (pblk.pprint(), dt)
 
    # print '-PBLK-------------------------------------'
    # print pblk.pprint()
    # print '------------------------------------------'
    # print '-BLK--------------------------------------'
    # print blk.pprint()
    # print '------------------------------------------'

   # topBlock = TheBDM.getTopBlockHeight()

   # print '\n\nCurrent Top Block is:', topBlock
   # TheBDM.getTopBlockHeader().pprint()

   # PyBlock
   # ==============
   # version =
   # hashPrev = Prev Hash
   # hashMerkleRoot = MerkleRoot
   # nTime = Timestamp
   # nBits
   # nNonce = Nonce
   # transactions = getTxRefPtrList()

   # PyTx
   # ==============
   # hash = TxHash
   # version = Version
   # lockTime = LockTime
   #      = nInputs
   #      = nOutputs

   # PyTxIn
   # ==============
   # prevout_hash <=> PrevTxHash
   # prevout_n <=> TxOutIndex
   # scriptSig <=> Script
   # sequence <=> Seq

   # PyTxOut
   # value <=> Value
   # scriptPubKey <= Script: PubKey() OP_CHECKSIG??
   def ptx_print(self, tx, dt, writeout=False):
      # out = 'TImeslot dt: ', dt
      out = ''
      # out = {}
      ptx = PyTx().fromCpp(tx)
      tx_hash = ptx.getHashHex(endianness=BIGENDIAN)
      n_inputs = len(ptx.inputs)
      n_outputs = len(ptx.outputs)
      lock_time = ptx.lockTime

      # Get TxIn info
      for i in range(tx.getNumTxIn()):
         ptxin = PyTxIn().fromCpp(tx.getTxIn(i))
         # print '==> TxIn:', i
         # print ptxin.pprint()
         prev_tx_hash = binary_to_hex(ptxin.outpoint.txHash, BIGENDIAN)

         # Only if writing
         if not writeout: continue

         if prev_tx_hash == self.coinbase:
            out = ''.join([out, 'in\t' + tx_hash + '\tcoinbase\t' + dt + '\n'])
         else:
            inAddr160 = TxInScriptExtractAddr160IfAvail(ptxin)

            pk = '(None)'
            if len(inAddr160)>0:
               pk = hash160_to_addrStr(inAddr160)

            # if pk == '(None)' and prev_tx_hash != self.coinbase:
            #    print '========================= '
            #    print '(None) ==== ', inAddr160
            #    print 'tx_hash ', tx_hash
            #    print 'ptx_hash ', prev_tx_hash
            #    print '========================= '
            #    # print ptx.pprint()
            #    # print '========================= '
            #    # print ptxin.pprint()
            #    sys.exit(1)

            out = ''.join([out, 'in\t' + tx_hash + '\t' + prev_tx_hash + '\t' + \
                           str(ptxin.outpoint.txOutIndex) + '\t' + \
                           pk + '\t' + dt + '\n'])
            # print out

      index = 0;
      for i in range(tx.getNumTxOut()):
         ptxout = PyTxOut().fromCpp(tx.getTxOut(i))
         # print '==> TxOut:', i
         # print ptxout.pprint()
         recip = hash160_to_addrStr(tx.getTxOut(i).getRecipientAddr());       
         pk = TxOutScriptExtractAddrStr(ptxout.binScript)
         if writeout:
            out = ''.join([out, 'out\t' + tx_hash + '\t' + str(index) + '\t' + pk + \
                           '\t' + str(float(ptxout.value)/1.0e8) + '\t' + dt + '\n'])
         index += 1

         # if prev_tx_hash == self.coinbase:
         #    self.pk_dict[str(tx_hash)] = str(pk)
            # print '========================= '
            # print 'Prev Tx Hash: ', prev_tx_hash
            # print 'Tx Hash: ', tx_hash
            # print 'pk: ', pk, tx.getNumTxOut()
            # print '========================= '

      return out

   def load_block(self, blockj, writeout=False):
      # Block info
      blk = self.bdm.getHeaderByHeight(blockj)
      pblk = PyBlockHeader().fromCpp(blk)
     
      if self.verbose:
         print '-PBLK-------------------------------------'
         print pblk.pprint()
         print '------------------------------------------'
      # print '-BLK--------------------------------------'
      # print blk.pprint()
      # print '------------------------------------------'
      dt = get_block_datetime(pblk.timestamp)

      # Block tx list
      # Tracer()()
      txList = blk.getTxRefPtrList()
      # print '===> TxLIst: ', len(txList)

      # For each tx in list
      out = []
      for txref in txList:
         tx = txref.getTxCopy()
         # print '=========================='
         try:
            if writeout:
               out.append(self.ptx_print(tx, dt, writeout=True))
            else:
               self.ptx_print(tx, dt, writeout=False)
         except:
            pass
           

         if self.verbose:
            print '=========================='
            print out
            print '=========================='
         # print tx.pprint()
      return len(txList), ''.join(out)

if __name__ == "__main__":
   reader = BlockReader()   
   blocks = reader.load_block_chain();

   total_tx = 0;
   files = ['transactions-0.txt']
   f = open(files[-1], 'w')
   for idx,block in enumerate(range(min(blocks), max(blocks))):
      if idx % 1000 == 0 and idx > 0:
         sys.stdout.write('%i blocks (%i transactions), ' % (idx, total_tx))
         sys.stdout.flush()

      if idx % 10000 == 0 and idx > 0:
         f.close()
         files.append('transactions-%i.txt' % idx)
         f = open(files[-1], 'w')

      # Write out only if block in range
      writeout = block in blocks
      num_tx, info = reader.load_block(block, writeout=writeout)
      if writeout:
         total_tx += num_tx
         f.write(info)
   if f is not None: f.close()
   print 'Done: %i blocks (%i transactions) ' % (idx, total_tx)