diff --git a/chainlib/eth/block.py b/chainlib/eth/block.py index a791a35..8589586 100644 --- a/chainlib/eth/block.py +++ b/chainlib/eth/block.py @@ -9,6 +9,7 @@ from hexathon import ( add_0x, strip_0x, compact, + to_int as hex_to_int, ) # local imports @@ -153,8 +154,8 @@ txs: {} self.timestamp, datetime.datetime.fromtimestamp(self.timestamp), self.author, - self.fee_limit, - self.fee_cost, + hex_to_int(self.fee_limit), + hex_to_int(self.fee_cost), len(self.txs), ) diff --git a/chainlib/eth/cli/log.py b/chainlib/eth/cli/log.py new file mode 100644 index 0000000..8e6cc88 --- /dev/null +++ b/chainlib/eth/cli/log.py @@ -0,0 +1 @@ +from chainlib.cli.log import process_log diff --git a/chainlib/eth/contract.py b/chainlib/eth/contract.py index 494d2a2..a24509d 100644 --- a/chainlib/eth/contract.py +++ b/chainlib/eth/contract.py @@ -6,6 +6,7 @@ import logging # external imports from hexathon import ( strip_0x, + add_0x, pad, ) @@ -502,6 +503,8 @@ def code(address, block_spec=BlockSpec.LATEST, id_generator=None): block_height = 'pending' else: block_height = int(block_spec) + block_height = block_height.to_bytes(8, byteorder='big') + block_height = add_0x(block_height.hex()) j = JSONRPCRequest(id_generator) o = j.template() o['method'] = 'eth_getCode' diff --git a/chainlib/eth/runnable/block.py b/chainlib/eth/runnable/block.py index 5f1fa09..c5bf867 100644 --- a/chainlib/eth/runnable/block.py +++ b/chainlib/eth/runnable/block.py @@ -65,6 +65,30 @@ def process_config_local(config, arg, args, flags): return config +def process_settings_local(settings, config): + block_identifier = config.get('_BLOCK') + maybe_hex = None + is_number = False + try: + maybe_hex = strip_0x(block_identifier) + except ValueError: + is_number = True + + if maybe_hex != None: + if len(maybe_hex) != 64: + is_number = True + else: + is_number = True + + r = None + if not is_number: + config.add(block_identifier, '_HASH', False) + else: + settings.set('_BLOCK', int(block_identifier)) + + return process_settings(settings, config) + + argparser = chainlib.eth.cli.ArgumentParser() arg_flags = ArgFlag() arg = Arg(arg_flags) @@ -82,32 +106,31 @@ config = process_config_local(config, arg, args, flags) logg.debug('config loaded:\n{}'.format(config)) settings = ChainSettings() -settings = process_settings(settings, config) +settings = process_settings_local(settings, config) logg.debug('settings loaded:\n{}'.format(settings)) -def get_block(conn, block_identifier, id_generator): - maybe_hex = None +def get_block(settings): + hsh = settings.get('HASH') r = None - try: - maybe_hex = strip_0x(block_identifier) - except ValueError: - r = get_block_number(conn, block_identifier, id_generator) - - if maybe_hex != None: - if len(maybe_hex) != 64: - r = get_block_number(conn, block_identifier, id_generator) - elif maybe_hex != block_identifier: - r = get_block_hash(conn, block_identifier, id_generator) + if hsh == None: + r = get_block_number( + settings.get('CONN'), + settings.get('_BLOCK'), + settings.get('RPC_ID_GENERATOR'), + ) else: - r = get_block_number(conn, block_identifier, id_generator) + r = get_block_hash( + settings.get('CONN'), + hsh, + settings.get('RPC_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: @@ -131,11 +154,7 @@ def block_process(block_src): def main(): - r = get_block( - settings.get('CONN'), - config.get('_BLOCK'), - settings.get('RPC_ID_GENERATOR'), - ) + r = get_block(settings) if not config.true('_RAW'): r = r.to_human() diff --git a/chainlib/eth/runnable/gas.py b/chainlib/eth/runnable/gas.py index fe23854..0285f4c 100644 --- a/chainlib/eth/runnable/gas.py +++ b/chainlib/eth/runnable/gas.py @@ -49,7 +49,7 @@ logg = logging.getLogger() def process_config_local(config, arg, args, flags): config.add(args.data, '_DATA', False) - config.add(args.amount, '_AMOUNT', False) + config.add(args.amount, '_VALUE', False) return config @@ -60,7 +60,7 @@ flags = arg_flags.STD_WRITE | arg_flags.WALLET argparser = chainlib.eth.cli.ArgumentParser() argparser = process_args(argparser, arg, flags) argparser.add_argument('--data', type=str, help='Transaction data') -argparser.add_argument('amount', type=int, help='Token amount to send') +argparser.add_argument('amount', type=str, help='Token amount to send') args = argparser.parse_args() logg = process_log(args, logg) @@ -99,7 +99,7 @@ def main(): if not config.true('_UNSAFE') and not is_checksum_address(recipient): raise ValueError('invalid checksum address') - logg.info('gas transfer from {} to {} value {}'.format(settings.get('SENDER_ADDRESS'), settings.get('RECIPIENT'), config.get('_AMOUNT'))) + logg.info('gas transfer from {} to {} value {}'.format(settings.get('SENDER_ADDRESS'), settings.get('RECIPIENT'), settings.get('VALUE'))) if logg.isEnabledFor(logging.DEBUG): try: sender_balance = balance( @@ -120,7 +120,7 @@ def main(): (tx_hash_hex, o) = g.create( settings.get('SENDER_ADDRESS'), settings.get('RECIPIENT'), - config.get('_AMOUNT'), + settings.get('VALUE'), data=config.get('_DATA'), id_generator=settings.get('RPC_ID_GENERATOR'), ) diff --git a/chainlib/eth/runnable/get.py b/chainlib/eth/runnable/get.py index e9325a4..522a47e 100644 --- a/chainlib/eth/runnable/get.py +++ b/chainlib/eth/runnable/get.py @@ -30,6 +30,8 @@ from chainlib.eth.connection import EthHTTPConnection from chainlib.eth.tx import ( Tx, pack, + transaction, + receipt, ) from chainlib.eth.address import ( to_checksum_address, @@ -51,6 +53,7 @@ from chainlib.eth.cli.config import ( Config, process_config, ) +from chainlib.eth.contract import code from chainlib.eth.cli.log import process_log from chainlib.eth.settings import process_settings @@ -66,6 +69,18 @@ def process_config_local(config, arg, args, flags): return config +def process_settings_local(settings, config): + item = config.get('_ITEM') + item = strip_0x(item) + + if len(item) == 40: + config.add(item, '_RECIPIENT', False) + elif len(item) == 64: + config.add(item, '_HASH', False) + + return process_settings(settings, config) + + arg_flags = ArgFlag() arg = Arg(arg_flags) flags = arg_flags.STD_BASE_READ | arg_flags.TARGET @@ -84,18 +99,18 @@ config = process_config_local(config, arg, args, flags) logg.debug('config loaded:\n{}'.format(config)) settings = ChainSettings() -settings = process_settings(settings, config) +settings = process_settings_local(settings, config) logg.debug('settings loaded:\n{}'.format(settings)) def get_transaction(conn, chain_spec, tx_hash, id_generator): - tx_hash = add_0x(tx_hash) - j = JSONRPCRequest(id_generator=id_generator) - o = j.template() - o['method'] = 'eth_getTransactionByHash' - o['params'].append(tx_hash) - o = j.finalize(o) + o = transaction(tx_hash, id_generator=id_generator) +# j = JSONRPCRequest(id_generator=id_generator) +# o = j.template() +# o['method'] = 'eth_getTransactionByHash' +# o['params'].append(tx_hash) +# o = j.finalize(o) tx_src = conn.do(o) if tx_src == None: logg.error('Transaction {} not found'.format(tx_hash)) @@ -109,10 +124,11 @@ def get_transaction(conn, chain_spec, tx_hash, id_generator): status = -1 rcpt = None - o = j.template() - o['method'] = 'eth_getTransactionReceipt' - o['params'].append(tx_hash) - o = j.finalize(o) + o = receipt(tx_hash, id_generator=id_generator) +# o = j.template() +# o['method'] = 'eth_getTransactionReceipt' +# o['params'].append(tx_hash) +# o = j.finalize(o) rcpt = conn.do(o) if tx == None: @@ -130,17 +146,17 @@ def get_transaction(conn, chain_spec, tx_hash, id_generator): def get_address(conn, address, id_generator, height): - address = add_0x(address) - j = JSONRPCRequest(id_generator=id_generator) - o = j.template() - o['method'] = 'eth_getCode' - o['params'].append(address) - height = to_blockheight_param(height) - o['params'].append(height) - o = j.finalize(o) - code = conn.do(o) + o = code(address, height, id_generator=id_generator) +# j = JSONRPCRequest(id_generator=id_generator) +# o = j.template() +# o['method'] = 'eth_getCode' +# o['params'].append(address) +# height = to_blockheight_param(height) +# o['params'].append(height) +# o = j.finalize(o) + r = conn.do(o) - content = strip_0x(code, allow_empty=True) + content = strip_0x(r, allow_empty=True) if len(content) == 0: return None @@ -149,25 +165,21 @@ def get_address(conn, address, id_generator, height): def main(): r = None - if len(config.get('_ITEM')) > 42: + if settings.get('HASH') != None: r = get_transaction( settings.get('CONN'), settings.get('CHAIN_SPEC'), - config.get('_ITEM'), + settings.get('HASH'), settings.get('RPC_ID_GENERATOR'), ) if not config.true('_RAW'): r = r.to_human() else: - if config.true('_UNSAFE'): - address = to_checksum_address(config.get('_ITEM')) - elif not is_checksum_address(config.get('_ITEM')): - raise ValueError('invalid checksum address: {}'.format(config.get('_ITEM'))) r = get_address( settings.get('CONN'), - config.get('_ITEM'), + settings.get('RECIPIENT'), settings.get('RPC_ID_GENERATOR'), - config.get('_HEIGHT'), + settings.get('HEIGHT'), ) if r != None: print(r) diff --git a/chainlib/eth/runnable/wait.py b/chainlib/eth/runnable/wait.py index 4b16d94..3941969 100644 --- a/chainlib/eth/runnable/wait.py +++ b/chainlib/eth/runnable/wait.py @@ -10,8 +10,14 @@ import logging import urllib # external imports +from chainlib.settings import ChainSettings from funga.eth.signer import EIP155Signer from funga.eth.keystore.dict import DictKeystore +from chainlib.chain import ChainSpec +from chainlib.jsonrpc import ( + JSONRPCRequest, + IntSequenceGenerator, + ) from hexathon import ( add_0x, strip_0x, @@ -21,10 +27,6 @@ from hexathon import ( # local imports from chainlib.eth.address import to_checksum from chainlib.eth.connection import EthHTTPConnection -from chainlib.jsonrpc import ( - JSONRPCRequest, - IntSequenceGenerator, - ) from chainlib.eth.nonce import ( RPCNonceOracle, OverrideNonceOracle, @@ -38,7 +40,6 @@ from chainlib.eth.tx import ( raw, ) from chainlib.eth.error import RevertEthException -from chainlib.chain import ChainSpec from chainlib.eth.runnable.util import decode_for_puny_humans from chainlib.eth.jsonrpc import to_blockheight_param import chainlib.eth.cli @@ -52,6 +53,7 @@ from chainlib.eth.cli.config import ( process_config, ) from chainlib.eth.cli.log import process_log +from chainlib.eth.settings import process_settings logg = logging.getLogger() diff --git a/chainlib/eth/settings.py b/chainlib/eth/settings.py index f5cc813..be4d75b 100644 --- a/chainlib/eth/settings.py +++ b/chainlib/eth/settings.py @@ -1,10 +1,15 @@ # external imports from chainlib.settings import process_settings as base_process_settings from chainlib.error import SignerMissingException -from hexathon import add_0x +from hexathon import ( + add_0x, + strip_0x, + ) +from chainlib.block import BlockSpec # local imports import chainlib.eth.cli +from chainlib.eth.address import to_checksum_address def process_settings_rpc(settings, config): @@ -29,6 +34,27 @@ def process_settings_rpc(settings, config): settings.set('CONN', conn) settings.set('RPC_ID_GENERATOR', rpc.id_generator) settings.set('RPC_SEND', config.true('_RPC_SEND')) + + return settings + + +def process_settings_blockspec(settings, config): + blockspec_in = None + try: + blockspec_in = config.get('_HEIGHT') + except KeyError: + return settings + + blockspec = None + if blockspec_in == 'latest': + blockspec = BlockSpec.LATEST + elif blockspec_in == 'pending': + blockspec = BlockSpec.PENDING + else: + blockspec = int(blockspec_in) + + settings.set('HEIGHT', blockspec) + return settings @@ -45,11 +71,10 @@ def process_settings_wallet(settings, config): if wallet.get_signer_address() == None and recipient_in != None: recipient_in = wallet.from_address(recipient_in) - recipient = add_0x(recipient_in) - + recipient = to_checksum_address(recipient_in) if not config.true('_UNSAFE') and recipient != recipient_in: raise ValueError('invalid checksum address: {}'.format(recipient_in)) - + recipient = add_0x(recipient) settings.set('WALLET', wallet) settings.set('RECIPIENT', recipient) @@ -63,17 +88,51 @@ def process_settings_contract(settings, config): except KeyError: return settings - exec_address = add_0x(exec_address_in) - + exec_address = to_checksum_address(exec_address_in) if not config.true('_UNSAFE') and exec_address != exec_address_in: raise ValueError('invalid checksum address: {}'.format(exec_address_in)) + exec_address = add_0x(exec_address) settings.set('EXEC', exec_address) return settings +def process_settings_data(settings, config): + data = None + try: + data = config.get('_DATA') + except KeyError: + return settings + + data = add_0x(config.get('_DATA')) + settings.set('DATA', data) + + return settings + + +def process_settings_hash(settings, config): + hsh = None + try: + hsh = config.get('_HASH') + except KeyError: + return settings + + hsh = strip_0x(hsh) + l = len(hsh) + if l != 64: + raise ValueError('invalid hash length {} for {}'.format(l, hsh)) + + hsh = add_0x(hsh) + settings.set('HASH', hsh) + + return settings + + def process_settings(settings, config): settings = base_process_settings(settings, config) settings = process_settings_wallet(settings, config) settings = process_settings_rpc(settings, config) + settings = process_settings_blockspec(settings, config) + settings = process_settings_data(settings, config) + settings = process_settings_hash(settings, config) return settings diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..114828d --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,51 @@ +# standard imports +import unittest +import os +import logging + +# external imports +from aiee.arg import process_args + +# local imports +#from chainlib.cli.base import argflag_std_base +from chainlib.eth.cli.arg import ( + ArgFlag, + Arg, + ArgumentParser, + ) +from chainlib.eth.cli.config import ( + Config, + process_config, + ) +script_dir = os.path.dirname(os.path.realpath(__file__)) +data_dir = os.path.join(script_dir, 'testdata') +config_dir = os.path.join(data_dir, 'config') + +logging.basicConfig(level=logging.DEBUG) + + +class TestCli(unittest.TestCase): + + def setUp(self): + self.flags = ArgFlag() + self.arg = Arg(self.flags) + + + def test_args_process_single(self): + ap = ArgumentParser() + flags = self.flags.VERBOSE | self.flags.CONFIG + process_args(ap, self.arg, flags) + + argv = [ + '-vv', + '-n', + 'foo', + ] + args = ap.parse_args(argv) + config = Config(config_dir) + config = process_config(config, self.arg, args, flags) + self.assertEqual(config.get('CONFIG_USER_NAMESPACE'), 'foo') + + +if __name__ == '__main__': + unittest.main()