# standard imports import logging # external imports import chainlib.eth.cli from chainlib.chain import ChainSpec from chainsyncer.backend.file import FileBackend from chainlib.interface import ChainInterface from chainlib.eth.block import ( block_latest, block_by_number, Block, ) from chainlib.eth.tx import ( receipt, Tx, ) from chainsyncer.driver.history import HistorySyncer from chainsyncer.driver.head import HeadSyncer from hexathon import ( strip_0x, uniform as hex_uniform, ) logging.basicConfig(level=logging.WARNING) logg = logging.getLogger() arg_flags = chainlib.eth.cli.argflag_std_read argparser = chainlib.eth.cli.ArgumentParser(arg_flags) argparser.add_argument('--start', type=int, help='start at block') argparser.add_argument('--end', type=int, help='end block (not inclusive)') argparser.add_argument('--interval', type=int, default=5, help='syncer poll interval for new blocks') argparser.add_argument('-d', type=str, required=True, help='output directory') argparser.add_argument('--sender', type=str, action='append', default=[], help='sender address sender to monitor') argparser.add_argument('--recipient', type=str, action='append', default=[], help='recipient address sender to monitor') argparser.add_argument('--address', type=str, action='append', default=[], help='sender or recipient address to monitor') args = argparser.parse_args() extra_args = { 'start': None, 'end': None, 'address': None, 'sender': None, 'recipient': None, 'd': '_OUTPUT_DIR', 'interval': 'SYNCER_LOOP_INTERVAL', } # process config config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args) logg.debug('config loaded\n'+ str(config)) # set up rpc rpc = chainlib.eth.cli.Rpc() conn = rpc.connect_by_config(config) chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC')) class EthChainInterface(ChainInterface): def __init__(self): self._block_by_number = block_by_number self._block_from_src = Block.from_src self._tx_receipt = receipt self._src_normalize = Tx.src_normalize class GasAddFilter: def __init__(self, chain_spec, senders, recipients): self.senders = senders self.recipients = recipients self.tx_gas = {} self.gas_sum = 0 self.match_label = None if len(senders) == 0 and len(recipients) == 0: self.match_label = 'match' def filter(self, conn, block, tx, db_session): sender = hex_uniform(strip_0x(tx.outputs[0])) recipient = hex_uniform(strip_0x(tx.inputs[0])) match_label = self.match_label if match_label == None: if sender in self.senders: match_label = 'sender ' + sender elif recipient in self.recipients: match_label = 'recipient ' + receipient self.gas_sum += tx.gas_used self.tx_gas[tx.hash] = tx.gas_used logg.info('{} tx {} ({}/{}) gas {} new sum {}'.format(match_label, tx.hash, tx.block.number, tx.index, tx.gas_used, self.gas_sum)) def sum(self): return self.gas_sum def main(): loop_interval = config.get('SYNCER_LOOP_INTERVAL') start = config.get('_START') if start == None: o = block_latest() r = conn.do(o) block_current = int(r, 16) start = block_current + 1 end = config.get('_END') syncer = None chain_interface = EthChainInterface() if end != None: backend = FileBackend.initial(chain_spec, end, start_block_height=start, base_dir=config.get('_OUTPUT_DIR')) syncer = HistorySyncer(backend, chain_interface) else: backend = FileBackend.live(chain_spec, start, base_dir=config.get('_OUTPUT_DIR')) syncer = HeadSyncer(backend, chain_interface) senders = [] recipients = [] for address in config.get('_SENDER'): clean_address = hex_uniform(strip_0x(address)) senders.append(clean_address) logg.debug('monitoring sender {}'.format(clean_address)) for address in config.get('_RECIPIENT'): clean_address = hex_uniform(strip_0x(address)) recipients.append(clean_address) logg.debug('monitoring recipient {}'.format(clean_address)) for address in config.get('_ADDRESS'): clean_address = hex_uniform(strip_0x(address)) if address not in senders: senders.append(clean_address) logg.debug('monitoring sender {}'.format(clean_address)) if address not in recipients: recipients.append(clean_address) logg.debug('monitoring recipient {}'.format(clean_address)) gas_filter = GasAddFilter(chain_spec, senders, recipients) syncer.add_filter(gas_filter) r = syncer.loop(config.get('SYNCER_LOOP_INTERVAL'), conn) for k in gas_filter.tx_gas.keys(): print('tx {} gas {}'.format(k, gas_filter.tx_gas[k])) print('total gas: ' + str(gas_filter.sum())) if __name__ == '__main__': main()