33 Commits

Author SHA1 Message Date
lash
a392f80440 Block number in cli accept, nonce bootstrap non-pending 2022-05-21 21:06:43 +00:00
lash
310ace8244 Apply block number accept for block cli, non-pending nonce bootstrap 2022-05-21 21:05:28 +00:00
lash
772781c1b8 Correct tx status human output 2022-05-18 22:01:59 +00:00
lash
bf166af544 Fix crash in human tx render on null receipt, order of rpc in rpc settings process 2022-05-14 20:36:09 +00:00
lash
43f3aff255 Compact gas price limit fields in tx json 2022-05-14 20:05:32 +00:00
lash
586eccfba0 Remove crashing logline in gas cli tool 2022-05-14 16:36:21 +00:00
lash
dfe18a4238 Make wallet settings processing pass without recipient set 2022-05-14 12:23:49 +00:00
lash
3f24ab93fc Add state config 2022-05-13 13:44:33 +00:00
lash
7ed616a13d Add contract process settings 2022-05-13 07:49:15 +00:00
lash
f22115ff8c Fix bug in handling of data and recipeint inputs 2022-05-12 18:42:33 +00:00
lash
7ea2b43d60 Update deps 2022-05-12 18:30:20 +00:00
lash
7dc62676ba Rehabilitate tests 2022-05-12 18:29:11 +00:00
lash
e03c5b8035 Implement wait tool on settings module 2022-05-12 18:20:52 +00:00
lash
6ad0edd8a8 Remove commented code 2022-05-12 18:07:33 +00:00
lash
e053098652 Implement get cli tool on settings modulewq 2022-05-12 18:05:13 +00:00
lash
e621913f74 Implement 'raw' cli tool on settings module 2022-05-12 16:13:25 +00:00
lash
3c55bc9ac2 Implement 'raw' cli tool on settings module 2022-05-12 16:13:02 +00:00
lash
65b10ea306 Implement 'raw' cli tool on settings module 2022-05-12 16:12:00 +00:00
lash
e723f26267 Implement info cli tool on settings module 2022-05-12 15:46:29 +00:00
lash
9470b81fad Implement decode tool on settings module 2022-05-12 15:19:16 +00:00
lash
2d06d60eed Implement block cli tool on settings module 2022-05-12 15:08:37 +00:00
lash
33d9877bac implement gas tool on new settings module 2022-05-12 14:08:36 +00:00
lash
05d3357318 Add settings module, implement for balance tool 2022-05-12 13:53:33 +00:00
lash
85e5359e7c Add settings module, implement for balance tool 2022-05-12 13:49:42 +00:00
lash
72f3b8d346 Rehabilitate encode tool 2022-05-12 13:22:23 +00:00
lash
21ebdeae77 Rehabilitate wait for tx tool 2022-05-12 13:05:02 +00:00
lash
628028e4ce Rehabilitate raw cli tool 2022-05-12 12:58:24 +00:00
lash
7604b48e12 Rehabilitate chain info tool 2022-05-12 11:23:00 +00:00
lash
d70dfc3f03 Rehabilitate decode tool 2022-05-12 11:10:47 +00:00
lash
c060222638 Rehabilitate gas cli tool on new chainlib 2022-05-12 09:29:10 +00:00
lash
e16cc956e0 Rehabilitate 'count' tool 2022-05-12 08:20:06 +00:00
lash
9012b9884a Apply balance tool on new chainlib setup 2022-05-12 06:27:16 +00:00
lash
a1896b40c1 Apply new arg and config handling from chainlib 2022-05-12 05:30:33 +00:00
29 changed files with 1010 additions and 328 deletions

View File

@@ -1,3 +1,11 @@
- 0.3.2
* Fix crash when supplying block number to block cli tool
* Allow non-pending nonce bootstrap
- 0.3.1
* Fix missing application of status on tx result
- 0.3.0
* Implement chainlib with new arg and config handling
* Implement cli tools on settings module
- 0.2.0
* Implement chainlib generic tx, block and tx result objects
- 0.1.3

View File

@@ -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
@@ -6,12 +9,15 @@ from hexathon import (
add_0x,
strip_0x,
compact,
to_int as hex_to_int,
)
# local imports
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 +93,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 +135,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,
hex_to_int(self.fee_limit),
hex_to_int(self.fee_cost),
len(self.txs),
)
return s

View File

@@ -1,10 +1,6 @@
# external imports
from chainlib.cli import (
ArgumentParser,
argflag_std_read,
argflag_std_write,
argflag_std_base,
argflag_std_base_read,
flag_reset as argflag_reset,
Flag,
from chainlib.cli.arg import (
ArgumentParser,
Arg,
ArgFlag,
process_args,
)

View File

@@ -2,7 +2,11 @@
import os
# external imports
from chainlib.cli import Config as BaseConfig
from chainlib.cli.config import (
Config as BaseConfig,
process_config as base_process_config,
)
script_dir = os.path.dirname(os.path.realpath(__file__))
data_dir = os.path.join(script_dir, '..')
@@ -14,14 +18,12 @@ class Config(BaseConfig):
default_base_config_dir = os.path.join(data_dir, 'data', 'config')
default_fee_limit = 21000
@classmethod
def from_args(cls, args, arg_flags=0x0f, env=os.environ, extra_args={}, base_config_dir=None, default_config_dir=None, user_config_dir=None, default_fee_limit=None, logger=None, load_callback=None):
super(Config, cls).override_defaults(base_dir=cls.default_base_config_dir)
if default_fee_limit == None:
default_fee_limit = cls.default_fee_limit
config = BaseConfig.from_args(args, arg_flags=arg_flags, env=env, extra_args=extra_args, base_config_dir=base_config_dir, default_config_dir=default_config_dir, user_config_dir=user_config_dir, default_fee_limit=default_fee_limit, logger=logger, load_callback=load_callback)
if not config.get('RPC_DIALECT'):
def process_config(config, arg, args, flags):
config = base_process_config(config, arg, args, flags)
if arg.match('provider', flags):
if not bool(config.get('RPC_DIALECT')):
config.add('default', 'RPC_DIALECT', exists_ok=True)
elif config.get('RPC_DIALECT') not in [
'openethereum',
@@ -29,5 +31,4 @@ class Config(BaseConfig):
]:
raise ValueError('unknown rpc dialect {}'.format(config.get('RPC_DIALECT')))
return config
return config

1
chainlib/eth/cli/log.py Normal file
View File

@@ -0,0 +1 @@
from chainlib.cli.log import process_log

View File

@@ -23,7 +23,7 @@ class Rpc(BaseRpc):
super(Rpc, self).__init__(EthHTTPConnection, wallet=wallet)
def connect_by_config(self, config):
def connect_by_config(self, config, nonce_confirmed=True):
"""
If the standard arguments for nonce and fee price/price have been defined (which generate the configuration keys "_NONCE", "_FEE_PRICE" and "_FEE_LIMIT" respectively) , the corresponding overrides for fee and nonce generators will be defined.
@@ -38,9 +38,9 @@ class Rpc(BaseRpc):
except KeyError:
pass
if nonce != None:
self.nonce_oracle = OverrideNonceOracle(self.get_sender_address(), nonce, id_generator=self.id_generator)
self.nonce_oracle = OverrideNonceOracle(self.get_sender_address(), nonce, confirmed=nonce_confirmed, id_generator=self.id_generator)
else:
self.nonce_oracle = RPCNonceOracle(self.get_sender_address(), self.conn, id_generator=self.id_generator)
self.nonce_oracle = RPCNonceOracle(self.get_sender_address(), self.conn, confirmed=nonce_confirmed, id_generator=self.id_generator)
fee_price = None
fee_limit = None

View File

@@ -15,6 +15,3 @@ class Wallet(BaseWallet):
"""
def __init__(self, checksummer=AddressChecksum):
super(Wallet, self).__init__(EIP155Signer, checksummer=checksummer, keystore=DictKeystore())

View File

@@ -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'

View File

@@ -5,10 +5,19 @@ credentials =
dialect = default
scheme = http
verify = 1
timeout = 10.0
proxy =
[chain]
spec = evm:berlin:1:ethereum
min_fee = 21000
max_fee = 8000000
[wallet]
key_file =
passphrase =
[state]
path =
runtime_path =
backend =

View File

@@ -42,12 +42,12 @@ class NonceOracle(BaseNonceOracle):
:param id_generator: json-rpc id generator
:type id_generator: chainlib.connection.JSONRPCIdGenerator
"""
def __init__(self, address, id_generator=None):
def __init__(self, address, id_generator=None, confirmed=None):
self.id_generator = id_generator
super(NonceOracle, self).__init__(add_0x(address))
super(NonceOracle, self).__init__(add_0x(address), confirmed=confirmed)
def get_nonce(self):
def get_nonce(self, confirmed=False):
"""Load initial nonce value.
"""
raise NotImplementedError('Class must be extended')
@@ -74,12 +74,12 @@ class RPCNonceOracle(NonceOracle):
:param id_generator: json-rpc id generator
:type id_generator: chainlib.connection.JSONRPCIdGenerator
"""
def __init__(self, address, conn, id_generator=None):
def __init__(self, address, conn, id_generator=None, confirmed=False):
self.conn = conn
super(RPCNonceOracle, self).__init__(address, id_generator=id_generator)
super(RPCNonceOracle, self).__init__(address, id_generator=id_generator, confirmed=confirmed)
def get_nonce(self):
def get_nonce(self, confirmed=False):
"""Load and return nonce value from network.
Note! First call to next_nonce after calling get_nonce will return the same value!
@@ -87,7 +87,7 @@ class RPCNonceOracle(NonceOracle):
:rtype: int
:returns: Initial nonce
"""
o = nonce(self.address, id_generator=self.id_generator)
o = nonce(self.address, confirmed=confirmed, id_generator=self.id_generator)
r = self.conn.do(o)
n = strip_0x(r)
return int(n, 16)
@@ -103,13 +103,13 @@ class OverrideNonceOracle(NonceOracle):
:param id_generator: json-rpc id generator (not used)
:type id_generator: chainlib.connection.JSONRPCIdGenerator
"""
def __init__(self, address, nonce, id_generator=None):
def __init__(self, address, nonce, id_generator=None, confirmed=False):
self.initial_nonce = nonce
self.nonce = self.initial_nonce
super(OverrideNonceOracle, self).__init__(address, id_generator=id_generator)
super(OverrideNonceOracle, self).__init__(address, id_generator=id_generator, confirmed=confirmed)
def get_nonce(self):
def get_nonce(self, confirmed=False):
"""Returns initial nonce value set at object construction.
:rtype: int

View File

@@ -10,51 +10,73 @@ from hexathon import (
strip_0x,
even,
)
# local imports
import chainlib.eth.cli
from chainlib.eth.address import AddressChecksum
from chainlib.settings import ChainSettings
from chainlib.chain import ChainSpec
from funga.eth.signer import EIP155Signer
from chainlib.jsonrpc import (
jsonrpc_result,
IntSequenceGenerator,
)
# 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.gas import (
OverrideGasOracle,
balance,
)
from chainlib.chain import ChainSpec
from funga.eth.signer import EIP155Signer
from chainlib.eth.settings import process_settings
logging.basicConfig(level=logging.WARNING)
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_read
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_positional('address', type=str, help='Ethereum address of recipient')
def process_config_local(config, arg, args, flags):
config.add(args.address, '_RECIPIENT', False)
return config
arg_flags = ArgFlag()
arg = Arg(arg_flags)
flags = arg_flags.STD_READ
argparser = chainlib.eth.cli.ArgumentParser()
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)
wallet = chainlib.eth.cli.Wallet()
wallet.from_config(config)
holder_address = args.address
if wallet.get_signer_address() == None and holder_address != None:
holder_address = wallet.from_address(holder_address)
logg = process_log(args, logg)
logg.debug('flags {} {} {}'.format(flags, arg_flags.SEQ, flags & arg_flags.SEQ))
rpc = chainlib.eth.cli.Rpc()
conn = rpc.connect_by_config(config)
config = Config()
config = process_config(config, arg, args, flags)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
settings = ChainSettings()
settings = process_settings(settings, config)
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
def main():
r = None
decimals = 18
o = balance(holder_address, id_generator=rpc.id_generator)
r = conn.do(o)
o = balance(settings.get('RECIPIENT'), id_generator=settings.get('RPC_ID_GENERATOR'))
r = settings.get('CONN').do(o)
hx = strip_0x(r)
balance_value = int(hx, 16)

View File

@@ -0,0 +1,172 @@
# 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
from chainlib.settings import ChainSettings
# 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
from chainlib.eth.settings import process_settings
logg = logging.getLogger()
script_dir = os.path.dirname(os.path.realpath(__file__))
config_dir = os.path.join(script_dir, '..', 'data', 'config')
def process_config_local(config, arg, args, flags):
config.add(args.block, '_BLOCK', False)
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)
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)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
settings = ChainSettings()
settings = process_settings_local(settings, config)
logg.debug('settings loaded:\n{}'.format(settings))
def get_block(settings):
hsh = None
try:
hsh = settings.get('HASH')[0]
except TypeError:
pass
r = None
if hsh == None:
r = get_block_number(
settings.get('CONN'),
settings.get('_BLOCK'),
settings.get('RPC_ID_GENERATOR'),
)
else:
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):
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():
r = get_block(settings)
if not config.true('_RAW'):
r = r.to_human()
else:
r = repr(r)
if r != None:
print(r)
if __name__ == '__main__':
main()

View File

@@ -8,43 +8,70 @@ import json
import logging
import select
# local imports
import chainlib.eth.cli
from chainlib.eth.address import AddressChecksum
from chainlib.eth.connection import EthHTTPConnection
from chainlib.eth.tx import count
# external imports
from chainlib.settings import ChainSettings
from chainlib.chain import ChainSpec
from chainlib.jsonrpc import IntSequenceGenerator
from funga.eth.keystore.dict import DictKeystore
from funga.eth.signer import EIP155Signer
from hexathon import add_0x
logging.basicConfig(level=logging.WARNING)
# 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
from chainlib.eth.settings import process_settings
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')
def process_config_local(config, arg, args, flags):
config.add(args.address, '_RECIPIENT', False)
return config
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)
holder_address = args.address
wallet = chainlib.eth.cli.Wallet()
wallet.from_config(config)
if wallet.get_signer_address() == None and holder_address != None:
wallet.from_address(holder_address)
logg = process_log(args, logg)
logg.debug('flags {} {} {}'.format(flags, arg_flags.SEQ, flags & arg_flags.SEQ))
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
conn = rpc.connect_by_config(config)
config = Config()
config = process_config(config, arg, args, flags)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
settings = ChainSettings()
settings = process_settings(settings, config)
logg.debug('settings loaded:\n{}'.format(settings))
def main():
# TODO: should tolerate if address not prefixed with 0x
o = count(add_0x(holder_address), id_generator=rpc.id_generator)
r = conn.do(o)
o = count(
settings.get('RECIPIENT'),
id_generator=settings.get('RPC_ID_GENERATOR'),
)
r = settings.get('CONN').do(o)
count_result = None
try:
count_result = int(r, 16)

View File

@@ -11,10 +11,24 @@ import select
# external imports
import chainlib.eth.cli
from chainlib.eth.tx import unpack
from chainlib.settings import ChainSettings
from chainlib.chain import ChainSpec
# local imports
import chainlib.eth.cli
from chainlib.eth.runnable.util import decode_for_puny_humans
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.settings import process_settings
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
@@ -22,16 +36,39 @@ 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.Flag.VERBOSE | chainlib.eth.cli.Flag.CHAIN_SPEC | chainlib.eth.cli.Flag.ENV_PREFIX | chainlib.eth.cli.Flag.RAW
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_positional('tx_data', type=str, help='Transaction data to decode')
args = argparser.parse_args()
config = chainlib.eth.cli.Config.from_args(args, arg_flags, default_config_dir=config_dir)
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
def process_config_local(config, arg, args, flags):
config.add(args.tx_data, '_TX_DATA', False)
return config
arg_flags = ArgFlag()
arg = Arg(arg_flags)
flags = arg_flags.VERBOSE | arg_flags.CHAIN_SPEC | arg_flags.RAW | arg_flags.ENV | arg_flags.SEQ
argparser = chainlib.eth.cli.ArgumentParser()
argparser = process_args(argparser, arg, flags)
argparser.add_argument('tx_data', type=str, help='Transaction data to decode')
args = argparser.parse_args()
logg = process_log(args, logg)
config = Config()
config = process_config(config, arg, args, flags)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
settings = ChainSettings()
settings = process_settings(settings, config)
logg.debug('settings loaded:\n{}'.format(settings))
def main():
decode_for_puny_humans(args.tx_data, chain_spec, sys.stdout)
decode_for_puny_humans(
config.get('_TX_DATA'),
settings.get('CHAIN_SPEC'),
sys.stdout,
)
if __name__ == '__main__':
main()

View File

@@ -21,6 +21,16 @@ from hexathon import (
# 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.cli.encode import CLIEncoder
from chainlib.eth.constant import ZERO_ADDRESS
from chainlib.eth.address import to_checksum
@@ -47,22 +57,28 @@ 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_write | chainlib.eth.cli.Flag.EXEC | chainlib.eth.cli.Flag.FEE | chainlib.eth.cli.Flag.FMT_HUMAN | chainlib.eth.cli.Flag.FMT_WIRE | chainlib.eth.cli.Flag.FMT_RPC
arg_flags = flag_reset(arg_flags, chainlib.cli.Flag.NO_TARGET)
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
def process_config_local(config, arg, args, flags):
config.add(args.signature, '_SIGNATURE', False)
config.add(args.contract_args, '_CONTRACT_ARGS', False)
return config
arg_flags = ArgFlag()
arg = Arg(arg_flags)
flags = arg_flags.STD_WRITE | arg_flags.EXEC | arg_flags.FEE | arg_flags.FMT_HUMAN | arg_flags.FMT_WIRE | arg_flags.FMT_RPC
argparser = chainlib.eth.cli.ArgumentParser()
argparser = process_args(argparser, arg, flags)
argparser.add_argument('--mode', type=str, choices=['tx', 'call', 'arg'], help='Mode of operation')
argparser.add_argument('--signature', type=str, help='Method signature to encode')
argparser.add_argument('contract_args', type=str, nargs='*', help='arguments to encode')
args = argparser.parse_args()
extra_args = {
'signature': None,
'contract_args': None,
}
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_config_dir=config_dir)
logg.debug('config loaded:\n{}'.format(config))
block_all = args.ww
block_last = args.w or block_all
logg = process_log(args, logg)
config = Config()
config = process_config(config, arg, args, flags)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
wallet = chainlib.eth.cli.Wallet(EIP155Signer)
wallet.from_config(config)

View File

@@ -14,56 +14,68 @@ from hexathon import (
add_0x,
strip_0x,
)
# local imports
from chainlib.eth.address import to_checksum_address
from chainlib.eth.connection import EthHTTPConnection
from chainlib.settings import ChainSettings
from chainlib.jsonrpc import (
JSONRPCRequest,
IntSequenceGenerator,
)
from chainlib.chain import ChainSpec
# local imports
from chainlib.eth.address import to_checksum_address
from chainlib.eth.connection import EthHTTPConnection
from chainlib.eth.gas import Gas
from chainlib.eth.gas import balance as gas_balance
from chainlib.chain import ChainSpec
from chainlib.eth.runnable.util import decode_for_puny_humans
from chainlib.eth.address import (
is_same_address,
is_checksum_address,
)
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.settings import process_settings
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.WALLET
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
def process_config_local(config, arg, args, flags):
config.add(args.data, '_DATA', False)
config.add(args.amount, '_VALUE', False)
return config
arg_flags = ArgFlag()
arg = Arg(arg_flags)
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_positional('amount', type=int, help='Token amount to send')
argparser.add_argument('amount', type=str, help='Token amount to send')
args = argparser.parse_args()
extra_args = {
'data': None,
'amount': None,
}
#config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_config_dir=config_dir)
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args)
block_all = args.ww
block_last = args.w or block_all
logg = process_log(args, logg)
wallet = chainlib.eth.cli.Wallet()
wallet.from_config(config)
config = Config()
config = process_config(config, arg, args, flags)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
conn = rpc.connect_by_config(config)
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
value = config.get('_AMOUNT')
send = config.true('_RPC_SEND')
settings = ChainSettings()
settings = process_settings(settings, config)
logg.debug('settings loaded:\n{}'.format(settings))
def balance(address, id_generator):
def balance(conn, address, id_generator):
o = gas_balance(address, id_generator=id_generator)
r = conn.do(o)
try:
@@ -75,47 +87,71 @@ def balance(address, id_generator):
def main():
signer = rpc.get_signer()
signer_address = rpc.get_sender_address()
g = Gas(chain_spec, signer=signer, gas_oracle=rpc.get_gas_oracle(), nonce_oracle=rpc.get_nonce_oracle())
g = Gas(
settings.get('CHAIN_SPEC'),
signer=settings.get('SIGNER'),
gas_oracle=settings.get('GAS_ORACLE'),
nonce_oracle=settings.get('NONCE_ORACLE'),
)
recipient = to_checksum_address(config.get('_RECIPIENT'))
if not config.true('_UNSAFE') and not is_checksum_address(recipient):
raise ValueError('invalid checksum address')
logg.info('gas transfer from {} to {} value {}'.format(signer_address, recipient, value))
if logg.isEnabledFor(logging.DEBUG):
try:
sender_balance = balance(add_0x(signer_address), rpc.id_generator)
recipient_balance = balance(add_0x(recipient), rpc.id_generator)
logg.debug('sender {} balance before: {}'.format(signer_address, sender_balance))
logg.debug('recipient {} balance before: {}'.format(recipient, recipient_balance))
sender_balance = balance(
settings.get('CONN'),
settings.get('SENDER_ADDRESS'),
settings.get('RPC_ID_GENERATOR'),
)
recipient_balance = balance(
settings.get('CONN'),
settings.get('RECIPIENT'),
settings.get('RPC_ID_GENERATOR'),
)
logg.debug('sender {} balance before: {}'.format(settings.get('SENDER_ADDRESS'), sender_balance))
logg.debug('recipient {} balance before: {}'.format(settings.get('RECIPIENT'), recipient_balance))
except urllib.error.URLError:
pass
(tx_hash_hex, o) = g.create(signer_address, add_0x(recipient), value, data=config.get('_DATA'), id_generator=rpc.id_generator)
(tx_hash_hex, o) = g.create(
settings.get('SENDER_ADDRESS'),
settings.get('RECIPIENT'),
settings.get('VALUE'),
data=config.get('_DATA'),
id_generator=settings.get('RPC_ID_GENERATOR'),
)
logg.info('gas transfer from {} to {} value {} hash {}'.format(settings.get('SENDER_ADDRESS'), settings.get('RECIPIENT'), settings.get('VALUE'), tx_hash_hex))
if send:
conn.do(o)
if block_last:
r = conn.wait(tx_hash_hex)
if settings.get('RPC_SEND'):
settings.get('CONN').do(o)
if config.true('_WAIT'):
r = settings.get('CONN').wait(tx_hash_hex)
if logg.isEnabledFor(logging.DEBUG):
sender_balance = balance(add_0x(signer_address), rpc.id_generator)
recipient_balance = balance(add_0x(recipient), rpc.id_generator)
logg.debug('sender {} balance after: {}'.format(signer_address, sender_balance))
logg.debug('recipient {} balance after: {}'.format(recipient, recipient_balance))
sender_balance = balance(
settings.get('CONN'),
settings.get('SENDER_ADDRESS'),
settings.get('RPC_ID_GENERATOR'),
)
recipient_balance = balance(
settings.get('CONN'),
settings.get('RECIPIENT'),
settings.get('RPC_ID_GENERATOR'),
)
logg.debug('sender {} balance before: {}'.format(settings.get('SENDER_ADDRESS'), sender_balance))
logg.debug('recipient {} balance before: {}'.format(settings.get('RECIPIENT'), recipient_balance))
if r['status'] == 0:
logg.critical('VM revert for {}. Wish I could tell you more'.format(tx_hash_hex))
sys.exit(1)
print(tx_hash_hex)
else:
#if logg.isEnabledFor(logging.INFO):
if config.true('_RAW'):
print(o['params'][0])
else:
io_str = io.StringIO()
decode_for_puny_humans(o['params'][0], chain_spec, io_str)
decode_for_puny_humans(o['params'][0], settings.get('CHAIN_SPEC'), io_str)
print(io_str.getvalue())

View File

@@ -23,12 +23,15 @@ from chainlib.jsonrpc import (
)
from chainlib.chain import ChainSpec
from chainlib.status import Status
from chainlib.settings import ChainSettings
# local imports
from chainlib.eth.connection import EthHTTPConnection
from chainlib.eth.tx import (
Tx,
pack,
transaction,
receipt,
)
from chainlib.eth.address import (
to_checksum_address,
@@ -41,35 +44,67 @@ 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.contract import code
from chainlib.eth.cli.log import process_log
from chainlib.eth.settings import process_settings
logging.basicConfig(level=logging.WARNING, format='%(asctime)s %(levelname)s %(filename)s:%(lineno)d %(message)s')
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')
def process_config_local(config, arg, args, flags):
config.add(args.item, '_ITEM', False)
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
flags = arg_flags.less(flags, arg_flags.CHAIN_SPEC)
argparser = chainlib.eth.cli.ArgumentParser()
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)
rpc = chainlib.eth.cli.Rpc()
conn = rpc.connect_by_config(config)
logg = process_log(args, logg)
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
config = Config()
config = process_config(config, arg, args, flags)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
item = add_0x(args.item)
settings = ChainSettings()
settings = process_settings_local(settings, config)
logg.debug('settings loaded:\n{}'.format(settings))
def get_transaction(conn, 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)
def get_transaction(conn, chain_spec, tx_hash, id_generator):
o = transaction(tx_hash, id_generator=id_generator)
tx_src = conn.do(o)
if tx_src == None:
logg.error('Transaction {} not found'.format(tx_hash))
@@ -83,12 +118,8 @@ def get_transaction(conn, 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)
rcpt = conn.do(o)
#status = int(strip_0x(rcpt['status']), 16)
if tx == None:
tx = Tx(tx_src)
@@ -105,17 +136,10 @@ def get_transaction(conn, 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)
r = conn.do(o)
content = strip_0x(code, allow_empty=True)
content = strip_0x(r, allow_empty=True)
if len(content) == 0:
return None
@@ -123,18 +147,24 @@ def get_address(conn, address, id_generator, height):
def main():
address = item
r = None
if len(address) > 42:
r = get_transaction(conn, address, rpc.id_generator)
if settings.get('HASH') != None:
hsh = settings.get('HASH')[0]
r = get_transaction(
settings.get('CONN'),
settings.get('CHAIN_SPEC'),
hsh,
settings.get('RPC_ID_GENERATOR'),
)
if not config.true('_RAW'):
r = r.to_human()
else:
if config.get('_UNSAFE'):
address = to_checksum_address(address)
elif not is_checksum_address(address):
raise ValueError('invalid checksum address: {}'.format(address))
r = get_address(conn, address, rpc.id_generator, config.get('_HEIGHT'))
r = get_address(
settings.get('CONN'),
settings.get('RECIPIENT'),
settings.get('RPC_ID_GENERATOR'),
settings.get('HEIGHT'),
)
if r != None:
print(r)

View File

@@ -17,6 +17,7 @@ from hexathon import (
)
import sha3
from funga.eth.signer import EIP155Signer
from chainlib.settings import ChainSettings
# local imports
from chainlib.eth.address import AddressChecksum
@@ -35,10 +36,20 @@ from chainlib.eth.gas import (
price,
)
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.settings import process_settings
BLOCK_SAMPLES = 10
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
script_dir = os.path.dirname(os.path.realpath(__file__))
@@ -54,34 +65,37 @@ results_translation = {
}
arg_flags = chainlib.eth.cli.argflag_std_read
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
def process_config_local(config, arg, args, flags):
config.add(args.local, '_LOCAL', False)
config.add(args.long, '_LONG', False)
config.add(args.entry, '_ENTRY', False)
if config.get('_ENTRY') != None:
if config.get('_ENTRY') not in results_translation.keys():
raise ValueError('Unknown entry {}'.format(config.get('_ENTRY')))
return config
arg_flags = ArgFlag()
arg = Arg(arg_flags)
flags = arg_flags.STD_READ | arg_flags.ENV
argparser = chainlib.eth.cli.ArgumentParser()
argparser = process_args(argparser, arg, flags)
argparser.add_argument('--long', action='store_true', help='Calculate averages through sampling of blocks and txs')
argparser.add_argument('--local', action='store_true', help='Include local info')
argparser.add_positional('entry', required=False, help='Output single item')
argparser.add_argument('entry', nargs='?', help='Output single item')
args = argparser.parse_args()
extra_args = {
'local': None,
'long': None,
'entry': None,
}
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_config_dir=config_dir)
logg = process_log(args, logg)
if config.get('_ENTRY') != None:
if config.get('_ENTRY') not in results_translation.keys():
raise ValueError('Unknown entry {}'.format(config.get('_ENTRY')))
config = Config()
config = process_config(config, arg, args, flags)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
rpc = chainlib.eth.cli.Rpc()
conn = rpc.connect_by_config(config)
settings = ChainSettings()
settings = process_settings(settings, config)
logg.debug('settings loaded:\n{}'.format(settings))
token_symbol = 'eth'
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
human = not config.true('_RAW')
longmode = config.true('_LONG')
def set_result(results, k, v, w=sys.stdout):
kt = results_translation[k]
@@ -94,17 +108,16 @@ def set_result(results, k, v, w=sys.stdout):
def main():
human = not config.true('_RAW')
results = {}
o = network_id(id_generator=rpc.id_generator)
r = conn.do(o)
#if human:
# n = format(n, ',')
o = network_id(id_generator=settings.get('RPC_ID_GENERATOR'))
r = settings.get('CONN').do(o)
if set_result(results, 'network_id', r):
return
o = block_latest(id_generator=rpc.id_generator)
r = conn.do(o)
o = block_latest(id_generator=settings.get('RPC_ID_GENERATOR'))
r = settings.get('CONN').do(o)
try:
n = int(r, 16)
except ValueError:
@@ -115,17 +128,17 @@ def main():
if set_result(results, 'block', n):
return
o = block_by_number(first_block_number, False, id_generator=rpc.id_generator)
r = conn.do(o)
o = block_by_number(first_block_number, False, id_generator=settings.get('RPC_ID_GENERATOR'))
r = settings.get('CONN').do(o)
last_block = Block(r)
last_timestamp = last_block.timestamp
if longmode:
if config.true('_LONG'):
aggr_time = 0.0
aggr_gas = 0
for i in range(BLOCK_SAMPLES):
o = block_by_number(first_block_number-i, False, id_generator=rpc.id_generator)
r = conn.do(o)
o = block_by_number(first_block_number-i, False, id_generator=settings.get('RPC_ID_GENERATOR'))
r = settings.get('CONN').do(o)
block = Block(r)
aggr_time += last_block.timestamp - block.timestamp
@@ -144,8 +157,8 @@ def main():
if set_result(results, 'block_time', aggr_time / BLOCK_SAMPLES):
return
o = price(id_generator=rpc.id_generator)
r = conn.do(o)
o = price(id_generator=settings.get('RPC_ID_GENERATOR'))
r = settings.get('CONN').do(o)
n = int(r, 16)
if human:
n = format(n, ',')
@@ -154,7 +167,7 @@ def main():
if config.get('_LOCAL'):
o = syncing()
r = conn.do(o)
r = settings.get('CONN').do(o)
if set_result(results, 'syncing', r):
return

View File

@@ -10,13 +10,15 @@ import logging
import urllib
# external imports
import chainlib.eth.cli
from chainlib.settings import ChainSettings
from funga.eth.signer import EIP155Signer
from funga.eth.keystore.dict import DictKeystore
from hexathon import (
add_0x,
strip_0x,
)
from chainlib.error import SignerMissingException
from chainlib.chain import ChainSpec
# local imports
from chainlib.eth.address import to_checksum
@@ -37,75 +39,79 @@ from chainlib.eth.tx import (
TxFactory,
raw,
)
from chainlib.error import SignerMissingException
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
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.settings import process_settings
logging.basicConfig(level=logging.WARNING)
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_write | chainlib.eth.cli.Flag.EXEC
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
def process_config_local(config, arg, args, flags):
config.add(args.deploy, '_DEPLOY', False)
config.add(args.mode, '_MODE', False)
config.add(args.data, '_DATA', False)
return config
arg_flags = ArgFlag()
arg = Arg(arg_flags)
flags = arg_flags.STD_WRITE | arg_flags.EXEC
argparser = chainlib.eth.cli.ArgumentParser()
argparser = process_args(argparser, arg, flags)
argparser.add_argument('--deploy', action='store_true', help='Deploy data as contract')
argparser.add_argument('--mode', choices=['tx', 'call'], type=str, help='Mode of operation')
argparser.add_positional('data', type=str, help='Transaction data')
argparser.add_argument('data', type=str, help='Transaction data')
args = argparser.parse_args()
config = chainlib.eth.cli.Config.from_args(args, arg_flags, default_config_dir=config_dir)
block_all = args.ww
block_last = args.w or block_all
logg = process_log(args, logg)
wallet = chainlib.eth.cli.Wallet(EIP155Signer)
wallet.from_config(config)
config = Config()
config = process_config(config, arg, args, flags)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
conn = rpc.connect_by_config(config)
settings = ChainSettings()
settings = process_settings(settings, config)
logg.debug('settings loaded:\n{}'.format(settings))
send = config.true('_RPC_SEND')
chain_spec = None
try:
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
except AttributeError:
pass
def main():
if config.get('_EXEC_ADDRESS') != None or config.true('_DEPLOY'):
if not args.u and exec_address != exec_address:
raise ValueError('invalid checksum address')
signer_address = None
try:
signer = rpc.get_signer()
signer_address = rpc.get_signer_address()
except SignerMissingException:
pass
if config.get('_EXEC_ADDRESS') != None or args.deploy:
exec_address = None
if config.get('_EXEC_ADDRESS') != None:
exec_address = add_0x(to_checksum(config.get('_EXEC_ADDRESS')))
#if not args.u and exec_address != add_0x(exec_address):
if not args.u and exec_address != exec_address:
raise ValueError('invalid checksum address')
if signer_address == None:
j = JSONRPCRequest(id_generator=rpc.id_generator)
if settings.get('SENDER_ADDRESS'):
j = JSONRPCRequest(id_generator=settings.get('RPC_ID_GENERATOR'))
o = j.template()
o['method'] = 'eth_call'
o['params'].append({
'to': exec_address,
'from': signer_address,
'to': settings.get('EXEC'),
'from': settings.get('SENDER_ADDRESS'),
'value': '0x00',
'gas': add_0x(int.to_bytes(8000000, 8, byteorder='big').hex()), # TODO: better get of network gas limit
'gasPrice': '0x01',
'data': add_0x(args.data),
'data': add_0x(config.get('_DATA')),
})
height = to_blockheight_param(config.get('_HEIGHT'))
o['params'].append(height)
o = j.finalize(o)
r = conn.do(o)
r = settings.get('CONN').do(o)
try:
print(strip_0x(r))
except ValueError:
@@ -113,17 +119,26 @@ def main():
sys.exit(1)
else:
if chain_spec == None:
if settings.get('CHAIN_SPEC') == None:
raise ValueError('chain spec must be specified')
g = TxFactory(chain_spec, signer=rpc.get_signer(), gas_oracle=rpc.get_gas_oracle(), nonce_oracle=rpc.get_nonce_oracle())
tx = g.template(signer_address, exec_address, use_nonce=True)
if args.data != None:
tx = g.set_code(tx, add_0x(args.data))
g = TxFactory(
settings.get('CHAIN_SPEC'),
signer=settings.get('SIGNER'),
gas_oracle=settings.get('GAS_ORACLE'),
nonce_oracle=settings.get('NONCE_ORACLE'),
)
tx = g.template(
settings.get('SENDER_ADDRESS'),
settings.get('EXEC'),
use_nonce=True,
)
if config.get('_DATA') != None:
tx = g.set_code(tx, add_0x(config.get('_DATA')))
(tx_hash_hex, o) = g.finalize(tx, id_generator=rpc.id_generator)
if send:
r = conn.do(o)
r = settings.get('CONN').do(o)
print(r)
else:
if config.get('_RAW'):
@@ -131,11 +146,11 @@ def main():
print(o)
else:
o = raw(args.data, id_generator=rpc.id_generator)
if send:
r = conn.do(o)
if block_last:
r = conn.wait(tx_hash_hex)
o = raw(config.get('_DATA'), id_generator=settings.get('RPC_ID_GENERATOR'))
if settings.get('RPC_SEND'):
r = settings.get('CONN').do(o)
if config.true('_WAIT'):
r = settings.get('CONN').wait(tx_hash_hex)
if r['status'] == 0:
logg.critical('VM revert for {}. Wish I could tell you more'.format(tx_hash_hex))
sys.exit(1)

View File

@@ -10,9 +10,14 @@ import logging
import urllib
# external imports
import chainlib.eth.cli
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,
@@ -22,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,
@@ -39,37 +40,60 @@ 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
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.settings import process_settings
logging.basicConfig(level=logging.WARNING)
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_read
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
def process_config_local(config, arg, args, flags):
config.add(args.ignore, '_IGNORE', False)
config.add(args.ignore_all, '_IGNORE_ALL', False)
config.add(args.hashes, '_HASH', False)
return config
def process_settings_local(settings, config):
settings.set('HASH', config.get('_HASH'))
return settings
arg_flags = ArgFlag()
arg = Arg(arg_flags)
flags = arg_flags.STD_READ
argparser = chainlib.eth.cli.ArgumentParser()
argparser = process_args(argparser, arg, flags)
argparser.add_argument('--ignore', type=str, action='append', default=[], help='Ignore error from the given transaction')
argparser.add_argument('--ignore-all', action='store_true', dest='ignore_all', help='Ignore errors from all transactions')
argparser.add_positional('hashes', append=True, type=str, help='Transaction hashes to wait for')
argparser.add_argument('hashes', nargs='*', type=str, help='Transaction hashes to wait for')
args = argparser.parse_args()
extra_args = {
'ignore': None,
'ignore_all': None,
'hashes': None,
}
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_config_dir=config_dir)
rpc = chainlib.eth.cli.Rpc()
conn = rpc.connect_by_config(config)
logg = process_log(args, logg)
chain_spec = None
try:
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
except AttributeError:
pass
config = Config()
config = process_config(config, arg, args, flags)
config = process_config_local(config, arg, args, flags)
logg.debug('config loaded:\n{}'.format(config))
settings = ChainSettings()
settings = process_settings(settings, config)
logg.debug('settings loaded:\n{}'.format(settings))
def main():
@@ -80,29 +104,30 @@ def main():
for hsh in config.get('_IGNORE'):
hashes_ignore.append(add_0x(hex_uniform(strip_0x(hsh))))
if len(config.get('_HASHES')) == 1:
if len(settings.get('HASH')) == 1:
hsh = settings.get('HASH')[0]
try:
hsh = add_0x(hex_uniform(strip_0x(config.get('_HASHES')[0])))
hashes_ready = [hsh]
except ValueError:
logg.debug('hash argument not a hash, will try it as a file name')
f = open(config.get('_HASHES')[0])
f = open(hsh)
for hsh in f:
logg.debug('hshs {}'.format(hsh))
hashes_ready.append(add_0x(hex_uniform(strip_0x(hsh.rstrip()))))
hashes_ready.append(hsh)
f.close()
else:
for hsh in config.get('_HASHES'):
logg.debug('hsh {}'.format(hsh))
hashes_ready.append(add_0x(hex_uniform(strip_0x(hsh))))
for hsh in settings.get('HASH'):
if hsh in hashes_ready:
logg.debug('skipping duplicate hash {}'.format(hsh))
continue
hashes_ready.append(hsh)
for hsh in hashes_ready:
logg.debug('processing transaction hash {}'.format(hsh))
logg.info('processing transaction hash {}'.format(hsh))
try:
r = conn.wait(hsh)
r = settings.get('CONN').wait(hsh)
except RevertEthException:
if config.get('_IGNORE_ALL') or hsh in hashes_ignore:
logg.info('ignoring revert in transaction hash {}'.format(hsh))
logg.debug('ignoring revert in transaction hash {}'.format(hsh))
continue
sys.stderr.write('revert in transaction hash {}\n'.format(hsh))
sys.exit(1)
@@ -110,5 +135,3 @@ def main():
if __name__ == '__main__':
main()

156
chainlib/eth/settings.py Normal file
View File

@@ -0,0 +1,156 @@
# external imports
from chainlib.settings import process_settings as base_process_settings
from chainlib.error import SignerMissingException
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):
rpc = chainlib.eth.cli.Rpc(settings.get('WALLET'))
conn = rpc.connect_by_config(config, nonce_confirmed=True)
settings.set('CONN', conn)
settings.set('RPC_ID_GENERATOR', rpc.id_generator)
settings.set('RPC_SEND', config.true('_RPC_SEND'))
gas_oracle = rpc.get_gas_oracle()
settings.set('GAS_ORACLE', gas_oracle)
try:
settings.set('SIGNER', rpc.get_signer())
sender_address = rpc.get_sender_address()
settings.set('SENDER_ADDRESS', add_0x(sender_address))
except AttributeError:
pass
except SignerMissingException:
pass
nonce_oracle = rpc.get_nonce_oracle()
settings.set('NONCE_ORACLE', nonce_oracle)
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
def process_settings_wallet(settings, config):
wallet = chainlib.eth.cli.Wallet()
wallet.from_config(config)
settings.set('WALLET', wallet)
recipient_in = None
try:
recipient_in = config.get('_RECIPIENT')
except KeyError:
return settings
if recipient_in == None:
return settings
if wallet.get_signer_address() == None and recipient_in != None:
recipient_in = wallet.from_address(recipient_in)
recipient_in = strip_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('RECIPIENT', recipient)
return settings
def process_settings_contract(settings, config):
exec_address_in = None
try:
exec_address_in = config.get('_EXEC_ADDRESS')
except KeyError:
return settings
if exec_address_in == None:
return settings
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
if data == None:
return settings
data = add_0x(data)
settings.set('DATA', data)
return settings
def process_settings_hash(settings, config):
hshs = None
try:
hshs = config.get('_HASH')
except KeyError:
return settings
if isinstance(hshs, str):
hshs = [hshs]
r = []
for hsh in hshs:
hsh = strip_0x(hsh)
l = len(hsh)
if l != 64:
raise ValueError('invalid hash length {} for {}'.format(l, hsh))
hsh = add_0x(hsh)
r.append(hsh)
settings.set('HASH', r)
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)
settings = process_settings_contract(settings, config)
return settings

View File

@@ -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)

View File

@@ -410,12 +410,14 @@ class TxFactory:
txe = EIP155Transaction(tx, tx['nonce'], tx['chainId'])
txes = txe.serialize()
gas_price = strip_0x(txes['gasPrice'])
gas_price = compact(gas_price)
gas = strip_0x(txes['gas'])
gas = compact(gas)
return {
'from': tx['from'],
'to': txes['to'],
'gasPrice': add_0x(compact(gas_price)),
'gas': add_0x(compact(gas)),
'gasPrice': add_0x(gas_price, compact_value=True),
'gas': add_0x(gas, compact_value=True),
'data': txes['data'],
}
@@ -656,7 +658,7 @@ class Tx(BaseTx, Src):
def apply_receipt(self, rcpt, strict=False):
result = TxResult(rcpt)
result = TxResult(src=rcpt)
self.apply_result(result)
@@ -747,13 +749,16 @@ input {}
self.payload,
)
if self.status != Status.PENDING:
status = Status.UNKNOWN.name
logg.debug('selfstatus {}'.format(self.status))
if self.result != None and self.result.status != Status.PENDING:
s += """gas_used {}
""".format(
self.result.fee_cost,
self.result.fee_cost,
)
status = self.result.status.name
s += 'status ' + self.status.name + '\n'
s += 'status ' + status + '\n'
if self.block != None:
s += """block_number {}

View File

@@ -3,5 +3,4 @@ pysha3==1.0.2
hexathon~=0.1.7
websocket-client==0.57.0
potaahto~=0.1.1
chainlib~=0.2.1
confini~=0.6.0
chainlib~=0.3.0

View File

@@ -1,6 +1,6 @@
[metadata]
name = chainlib-eth
version = 0.2.1
version = 0.3.2
description = Ethereum implementation of the chainlib interface
author = Louis Holbrook
author_email = dev@holbrook.no
@@ -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

View File

@@ -29,6 +29,7 @@ class TestBlock(unittest.TestCase):
tx_two_src_hash = os.urandom(32).hex()
block_hash = os.urandom(32).hex()
parent_hash = os.urandom(32).hex()
block_author = os.urandom(20).hex()
block_time = datetime.datetime.utcnow().timestamp()
block_src = {
@@ -40,6 +41,9 @@ class TestBlock(unittest.TestCase):
tx_two_src_hash,
],
'timestamp': block_time,
'gas_used': '0x1234',
'gas_limit': '0x2345',
'parent_hash': parent_hash
}
block = Block(block_src)

51
tests/test_cli.py Normal file
View File

@@ -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()

View File

@@ -21,6 +21,9 @@ class TestStat(unittest.TestCase):
'transactions': [],
'number': 41,
'author': os.urandom(20).hex(),
'gas_used': '0x1234',
'gas_limit': '0x2345',
'parent_hash': None,
})
d = datetime.datetime.utcnow()
@@ -30,6 +33,9 @@ class TestStat(unittest.TestCase):
'transactions': [],
'number': 42,
'author': os.urandom(20).hex(),
'gas_used': '0x1234',
'gas_limit': '0x2345',
'parent_hash': None,
})
s.block_apply(block_a)
@@ -43,6 +49,9 @@ class TestStat(unittest.TestCase):
'transactions': [],
'number': 43,
'author': os.urandom(20).hex(),
'gas_used': '0x1234',
'gas_limit': '0x2345',
'parent_hash': None,
})
s.block_apply(block_c)

View File

@@ -130,6 +130,9 @@ class TxTestCase(EthTesterCase):
'timestamp': 13241324,
'transactions': [],
'author': os.urandom(20).hex(),
'gas_used': 21000,
'gas_limit': '0x2345',
'parent_hash': None,
})
with self.assertRaises(AttributeError):
tx = Tx(tx_data, block=block)
@@ -173,6 +176,9 @@ class TxTestCase(EthTesterCase):
'timestamp': 13241324,
'transactions': [],
'author': os.urandom(20).hex(),
'gas_used': 21000,
'gas_limit': '0x2345',
'parent_hash': None,
})
block.txs = [add_0x(tx_data['hash'])]