eth-gas-sum

Sync a running total of gas usage across EVM blocks
git clone git://holbrook.no/ethd-gas-sum.git
Log | Files | Refs

sum.py (5081B)


      1 # standard imports
      2 import logging
      3 
      4 # external imports
      5 import chainlib.eth.cli
      6 from chainlib.chain import ChainSpec
      7 from chainsyncer.backend.file import FileBackend
      8 from chainlib.interface import ChainInterface
      9 from chainlib.eth.block import (
     10         block_latest,
     11         block_by_number,
     12         Block,
     13         )
     14 from chainlib.eth.tx import (
     15         receipt,
     16         Tx,
     17         )
     18 from chainsyncer.driver.history import HistorySyncer
     19 from chainsyncer.driver.head import HeadSyncer
     20 from hexathon import (
     21         strip_0x,
     22         uniform as hex_uniform,
     23         )
     24 
     25 
     26 logging.basicConfig(level=logging.WARNING)
     27 logg = logging.getLogger()
     28 
     29 
     30 arg_flags = chainlib.eth.cli.argflag_std_read
     31 argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
     32 argparser.add_argument('--start', type=int, help='start at block') 
     33 argparser.add_argument('--end', type=int, help='end block (not inclusive)') 
     34 argparser.add_argument('--interval', type=int, default=5, help='syncer poll interval for new blocks')
     35 argparser.add_argument('-d', type=str, required=True, help='output directory')
     36 argparser.add_argument('--sender', type=str, action='append', default=[], help='sender address sender to monitor')
     37 argparser.add_argument('--recipient', type=str, action='append', default=[], help='recipient address sender to monitor')
     38 argparser.add_argument('--address', type=str, action='append', default=[], help='sender or recipient address to monitor')
     39 args = argparser.parse_args()
     40 
     41 extra_args = {
     42     'start': None,
     43     'end': None,
     44     'address': None,
     45     'sender': None,
     46     'recipient': None,
     47     'd': '_OUTPUT_DIR',
     48     'interval': 'SYNCER_LOOP_INTERVAL',
     49         }
     50 # process config
     51 config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args)
     52 logg.debug('config loaded\n'+ str(config))
     53 
     54 # set up rpc
     55 rpc = chainlib.eth.cli.Rpc()
     56 conn = rpc.connect_by_config(config)
     57 
     58 chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
     59 
     60 
     61 class EthChainInterface(ChainInterface):
     62 
     63     def __init__(self):
     64         self._block_by_number = block_by_number
     65         self._block_from_src = Block.from_src
     66         self._tx_receipt = receipt
     67         self._src_normalize = Tx.src_normalize
     68 
     69 
     70 class GasAddFilter:
     71 
     72     def __init__(self, chain_spec, senders, recipients):
     73         self.senders = senders
     74         self.recipients = recipients
     75         self.tx_gas = {}
     76         self.gas_sum = 0
     77         self.match_label = None
     78         if len(senders) == 0 and len(recipients) == 0:
     79             self.match_label = 'match'
     80        
     81 
     82     def filter(self, conn, block, tx, db_session):
     83         sender = hex_uniform(strip_0x(tx.outputs[0]))
     84         recipient = hex_uniform(strip_0x(tx.inputs[0]))
     85         match_label = self.match_label
     86         if match_label == None: 
     87             if sender in self.senders:
     88                 match_label = 'sender ' + sender
     89             elif recipient in self.recipients:
     90                 match_label = 'recipient ' + receipient
     91         self.gas_sum += tx.gas_used
     92         self.tx_gas[tx.hash] = tx.gas_used
     93         logg.info('{} tx {} ({}/{}) gas {} new sum {}'.format(match_label, tx.hash, tx.block.number, tx.index, tx.gas_used, self.gas_sum))
     94 
     95     def sum(self):
     96         return self.gas_sum
     97 
     98 
     99 def main():
    100     loop_interval = config.get('SYNCER_LOOP_INTERVAL')
    101     start = config.get('_START')
    102     if start == None:
    103         o = block_latest()
    104         r = conn.do(o)
    105         block_current = int(r, 16)
    106         start = block_current + 1
    107     end = config.get('_END')
    108 
    109     syncer = None
    110     chain_interface = EthChainInterface()
    111     if end != None:
    112         backend = FileBackend.initial(chain_spec, end, start_block_height=start, base_dir=config.get('_OUTPUT_DIR'))
    113         syncer = HistorySyncer(backend, chain_interface)
    114     else:
    115         backend = FileBackend.live(chain_spec, start, base_dir=config.get('_OUTPUT_DIR'))
    116         syncer = HeadSyncer(backend, chain_interface)
    117 
    118     senders = []
    119     recipients = []
    120 
    121     for address in config.get('_SENDER'):
    122         clean_address = hex_uniform(strip_0x(address))
    123         senders.append(clean_address)
    124         logg.debug('monitoring sender {}'.format(clean_address))
    125     for address in config.get('_RECIPIENT'):
    126         clean_address = hex_uniform(strip_0x(address))
    127         recipients.append(clean_address)
    128         logg.debug('monitoring recipient {}'.format(clean_address))
    129     for address in config.get('_ADDRESS'):
    130         clean_address = hex_uniform(strip_0x(address))
    131         if address not in senders:
    132             senders.append(clean_address)
    133             logg.debug('monitoring sender {}'.format(clean_address))
    134         if address not in recipients:
    135             recipients.append(clean_address)
    136             logg.debug('monitoring recipient {}'.format(clean_address))
    137 
    138     gas_filter = GasAddFilter(chain_spec, senders, recipients)
    139     syncer.add_filter(gas_filter)
    140 
    141     r = syncer.loop(config.get('SYNCER_LOOP_INTERVAL'), conn)
    142     for k in gas_filter.tx_gas.keys():
    143         print('tx {} gas {}'.format(k, gas_filter.tx_gas[k]))
    144     print('total gas: ' + str(gas_filter.sum()))
    145 
    146 
    147 if __name__ == '__main__':
    148     main()