diff --git a/chainlib/eth/block.py b/chainlib/eth/block.py index d06ff1c..a791a35 100644 --- a/chainlib/eth/block.py +++ b/chainlib/eth/block.py @@ -1,4 +1,7 @@ -import sys +# standard imports +import logging +import datetime + # external imports from chainlib.jsonrpc import JSONRPCRequest from chainlib.block import Block as BaseBlock @@ -12,6 +15,8 @@ from hexathon import ( from chainlib.eth.tx import Tx from .src import Src +logg = logging.getLogger(__name__) + def block_latest(id_generator=None): """Implements chainlib.interface.ChainInterface method @@ -87,21 +92,29 @@ class Block(BaseBlock, Src): tx_generator = Tx - def __init__(self, src): - super(Block, self).__init__(src) - import sys - self.set_hash(src['hash']) + def __init__(self, src=None): + super(Block, self).__init__(src=src) + + self.set_hash(self.src['hash']) try: - self.number = int(strip_0x(src['number']), 16) + self.number = int(strip_0x(self.src['number']), 16) except TypeError: - self.number = int(src['number']) - self.txs = src['transactions'] - self.block_src = src + self.number = int(self.src['number']) + self.txs = self.src['transactions'] + self.block_src = self.src try: - self.timestamp = int(strip_0x(src['timestamp']), 16) + self.timestamp = int(strip_0x(self.src['timestamp']), 16) except TypeError: - self.timestamp = int(src['timestamp']) - self.author = src['author'] + self.timestamp = int(self.src['timestamp']) + + try: + self.author = self.src['author'] + except KeyError: + self.author = self.src['miner'] + + self.fee_limit = self.src['gas_limit'] + self.fee_cost = self.src['gas_used'] + self.parent_hash = self.src['parent_hash'] def tx_index_by_hash(self, tx_hash): @@ -121,3 +134,29 @@ class Block(BaseBlock, Src): if idx == -1: raise AttributeError('tx {} not found in block {}'.format(tx_hash, self.hash)) return idx + + + def to_human(self): + s = """hash: {} +number: {} +parent: {} +timestamp: {} +time: {} +author: {} +gas_limit: {} +gas_used: {} +txs: {} +""".format( + self.hash, + self.number, + self.parent_hash, + self.timestamp, + datetime.datetime.fromtimestamp(self.timestamp), + self.author, + self.fee_limit, + self.fee_cost, + len(self.txs), + ) + + return s + diff --git a/chainlib/eth/runnable/balance.py b/chainlib/eth/runnable/balance.py index 4a65775..8ef49e5 100644 --- a/chainlib/eth/runnable/balance.py +++ b/chainlib/eth/runnable/balance.py @@ -40,7 +40,7 @@ logg = logging.getLogger() script_dir = os.path.dirname(os.path.realpath(__file__)) -argparser = chainlib.eth.cli.ArgumentParser() #arg_flags) +argparser = chainlib.eth.cli.ArgumentParser() arg_flags = ArgFlag() arg = Arg(arg_flags) flags = arg_flags.STD_READ diff --git a/chainlib/eth/runnable/block.py b/chainlib/eth/runnable/block.py new file mode 100644 index 0000000..9d75173 --- /dev/null +++ b/chainlib/eth/runnable/block.py @@ -0,0 +1,138 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +# standard imports +import sys +import os +import json +import argparse +import logging +import enum +import select + +# external imports +from potaahto.symbols import snake_and_camel +from hexathon import ( + add_0x, + strip_0x, + ) +import sha3 +from chainlib.jsonrpc import ( + JSONRPCRequest, + jsonrpc_result, + IntSequenceGenerator, + ) +from chainlib.chain import ChainSpec +from chainlib.status import Status + +# local imports +from chainlib.eth.connection import EthHTTPConnection +from chainlib.eth.tx import ( + Tx, + pack, + ) +from chainlib.eth.address import ( + to_checksum_address, + is_checksum_address, + ) +from chainlib.eth.block import ( + Block, + block_by_hash, + block_by_number, + ) +from chainlib.eth.runnable.util import decode_for_puny_humans +from chainlib.eth.jsonrpc import to_blockheight_param +import chainlib.eth.cli +from chainlib.eth.cli.arg import ( + Arg, + ArgFlag, + process_args, + ) +from chainlib.eth.cli.config import ( + Config, + process_config, + ) +from chainlib.eth.cli.log import process_log + +logg = logging.getLogger() + +script_dir = os.path.dirname(os.path.realpath(__file__)) +config_dir = os.path.join(script_dir, '..', 'data', 'config') + +argparser = chainlib.eth.cli.ArgumentParser() +arg_flags = ArgFlag() +arg = Arg(arg_flags) +flags = arg_flags.STD_BASE_READ +flags = arg_flags.less(flags, arg_flags.CHAIN_SPEC) +argparser = process_args(argparser, arg, flags) +argparser.add_argument('block', nargs='?', type=str, help='Block hash or number to retrieve') +args = argparser.parse_args() + +logg = process_log(args, logg) + +config = Config() +config = process_config(config, arg, args, flags) +logg.debug('config loaded:\n{}'.format(config)) + +rpc = chainlib.eth.cli.Rpc() +conn = rpc.connect_by_config(config) + +chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC')) + +item = add_0x(args.block) + + +def get_block(conn, block_identifier, id_generator): + maybe_hex = None + r = None + try: + maybe_hex = strip_0x(block_identifier) + except ValueError: + r = get_block_number(conn, block_identifier, id_generator) + + if len(maybe_hex) != 64: + r = get_block_number(conn, block_identifier, id_generator) + + if maybe_hex != block_identifier: + r = get_block_hash(conn, block_identifier, id_generator) + else: + r = get_block_number(conn, block_identifier, id_generator) + + return block_process(r) + + +def get_block_number(conn, block_number, id_generator): + block_number = int(block_number) + o = block_by_number(block_number, include_tx=False) + block_src = conn.do(o) + if block_src == None: + logg.error('Block number {} not found'.format(block_number)) + sys.exit(1) + return block_src + +def get_block_hash(conn, block_hash, id_generator): + block_hash = add_0x(block_hash) + o = block_by_hash(block_hash, include_tx=False) + block_src = conn.do(o) + if block_src == None: + logg.error('Block hash {} not found'.format(block_hash)) + sys.exit(1) + return block_src + + +def block_process(block_src): + return Block(block_src) + + +def main(): + block_identifier = item + r = get_block(conn, block_identifier, rpc.id_generator) + if not config.true('_RAW'): + r = r.to_human() + else: + r = repr(r) + if r != None: + print(r) + + +if __name__ == '__main__': + main() diff --git a/chainlib/eth/runnable/count.py b/chainlib/eth/runnable/count.py index 6ad040a..4085901 100644 --- a/chainlib/eth/runnable/count.py +++ b/chainlib/eth/runnable/count.py @@ -10,6 +10,16 @@ import select # local imports import chainlib.eth.cli +from chainlib.eth.cli.arg import ( + Arg, + ArgFlag, + process_args, + ) +from chainlib.eth.cli.config import ( + Config, + process_config, + ) +from chainlib.eth.cli.log import process_log from chainlib.eth.address import AddressChecksum from chainlib.eth.connection import EthHTTPConnection from chainlib.eth.tx import count @@ -25,11 +35,21 @@ logg = logging.getLogger() script_dir = os.path.dirname(os.path.realpath(__file__)) config_dir = os.path.join(script_dir, '..', 'data', 'config') -arg_flags = chainlib.eth.cli.argflag_std_base_read | chainlib.eth.cli.Flag.WALLET -argparser = chainlib.eth.cli.ArgumentParser(arg_flags) -argparser.add_positional('address', type=str, help='Ethereum address of recipient') +argparser = chainlib.eth.cli.ArgumentParser() +arg_flags = ArgFlag() +arg = Arg(arg_flags) +flags = arg_flags.STD_BASE_READ | arg_flags.WALLET | arg_flags.UNSAFE +argparser = process_args(argparser, arg, flags) + +argparser.add_argument('address', type=str, help='Ethereum address of recipient') args = argparser.parse_args() -config = chainlib.eth.cli.Config.from_args(args, arg_flags, default_config_dir=config_dir) + +logg = process_log(args, logg) +logg.debug('flags {} {} {}'.format(flags, arg_flags.SEQ, flags & arg_flags.SEQ)) + +config = Config() +config = process_config(config, arg, args, flags) +logg.debug('config loaded:\n{}'.format(config)) holder_address = args.address wallet = chainlib.eth.cli.Wallet() diff --git a/chainlib/eth/runnable/get.py b/chainlib/eth/runnable/get.py index d0db644..90277d1 100644 --- a/chainlib/eth/runnable/get.py +++ b/chainlib/eth/runnable/get.py @@ -41,6 +41,16 @@ from chainlib.eth.block import ( from chainlib.eth.runnable.util import decode_for_puny_humans from chainlib.eth.jsonrpc import to_blockheight_param import chainlib.eth.cli +from chainlib.eth.cli.arg import ( + Arg, + ArgFlag, + process_args, + ) +from chainlib.eth.cli.config import ( + Config, + process_config, + ) +from chainlib.eth.cli.log import process_log logging.basicConfig(level=logging.WARNING, format='%(asctime)s %(levelname)s %(filename)s:%(lineno)d %(message)s') logg = logging.getLogger() @@ -48,12 +58,20 @@ logg = logging.getLogger() script_dir = os.path.dirname(os.path.realpath(__file__)) config_dir = os.path.join(script_dir, '..', 'data', 'config') -arg_flags = chainlib.eth.cli.argflag_std_base_read -arg_flags = chainlib.eth.cli.argflag_reset(arg_flags, chainlib.eth.cli.Flag.CHAIN_SPEC) -argparser = chainlib.eth.cli.ArgumentParser(arg_flags) -argparser.add_positional('item', type=str, help='Address or transaction to retrieve data for') +argparser = chainlib.eth.cli.ArgumentParser() +arg_flags = ArgFlag() +arg = Arg(arg_flags) +flags = arg_flags.STD_BASE_READ +flags = arg_flags.less(flags, arg_flags.CHAIN_SPEC) +argparser = process_args(argparser, arg, flags) +argparser.add_argument('item', type=str, help='Address or transaction to retrieve data for') args = argparser.parse_args() -config = chainlib.eth.cli.Config.from_args(args, arg_flags, default_config_dir=config_dir) + +logg = process_log(args, logg) + +config = Config() +config = process_config(config, arg, args, flags) +logg.debug('config loaded:\n{}'.format(config)) rpc = chainlib.eth.cli.Rpc() conn = rpc.connect_by_config(config) diff --git a/chainlib/eth/src.py b/chainlib/eth/src.py index dbf86d6..da22df3 100644 --- a/chainlib/eth/src.py +++ b/chainlib/eth/src.py @@ -1,5 +1,6 @@ # standard imports import logging +import json # external imports from potaahto.symbols import snake_and_camel @@ -46,3 +47,7 @@ class Src(BaseSrc): v = uniform(v, compact_value=False, allow_empty=True) return v + + + def __repr__(self): + return json.dumps(self.src) diff --git a/setup.cfg b/setup.cfg index 8d11aed..c626816 100644 --- a/setup.cfg +++ b/setup.cfg @@ -48,3 +48,4 @@ console_scripts = eth-info = chainlib.eth.runnable.info:main eth-nonce = chainlib.eth.runnable.count:main eth-wait = chainlib.eth.runnable.wait:main + eth-block = chainlib.eth.runnable.block:main