Compare commits

..

3 Commits

Author SHA1 Message Date
nolash
050b6a9aeb Merge remote-tracking branch 'origin/0.0.10-dev' into lash/args-only 2021-10-11 21:14:15 +02:00
nolash
9018aefcbe
Upgrade chainlib, switch to funga for signer 2021-10-11 18:21:46 +02:00
nolash
dee523bada
Add raw args option for tx factory 2021-10-10 17:56:30 +02:00
56 changed files with 181 additions and 1152 deletions

View File

@ -1,26 +1,3 @@
- 0.1.1
* Add fee_limit, fee_price alias to Tx object
- 0.1.0:
* Allow nonce ommission in encode when not tx mode
* Add rcpt src to tx object
- 0.0.27:
* Add man pages with chainlib man page generator helper
* Remove redundant arg flags from runnables: get
- 0.0.26:
* Remove manual 0x handling bug in tx
- 0.0.25:
* Upgrade chainlib to get passphrase file for wallet keyfile
- 0.0.24:
* Upgrade from hexathon bug breaking compact hex function
- 0.0.23:
* Make get block args and responses compatible with picky golang marhaling in geth
- 0.0.22:
* Enable unpack of pre EIP-155 transactions
* Allow missing status property of receipts in non-strict modes
- 0.0.21:
* Remove warnings from cytoolz/rlp in funga-eth
- 0.0.15:
* Correct inverted addess checksum check for gas cli
- 0.0.5-unreleased:
- 0.0.5-pending
* Receive all ethereum components from chainlib package
* Make settings configurable

View File

@ -1,16 +0,0 @@
PREFIX ?= /usr/local
BUILD_DIR = build/$(PREFIX)/share/man
man:
mkdir -vp $(BUILD_DIR)
chainlib-man.py -b `PYTHONPATH=. python chainlib/eth/runnable/flags.py gas` -v -n eth-gas -d $(BUILD_DIR)/ man
chainlib-man.py -b `PYTHONPATH=. python chainlib/eth/runnable/flags.py info` -v -n eth-info -d $(BUILD_DIR)/ man
chainlib-man.py -b `PYTHONPATH=. python chainlib/eth/runnable/flags.py get` -v -n eth-get -d $(BUILD_DIR)/ man
chainlib-man.py -b `PYTHONPATH=. python chainlib/eth/runnable/flags.py decode` -v -n eth-decode -d $(BUILD_DIR)/ man
chainlib-man.py -b `PYTHONPATH=. python chainlib/eth/runnable/flags.py encode` -v -n eth-encode -d $(BUILD_DIR)/ man
chainlib-man.py -b `PYTHONPATH=. python chainlib/eth/runnable/flags.py count` -v -n eth-count -d $(BUILD_DIR)/ man
chainlib-man.py -b `PYTHONPATH=. python chainlib/eth/runnable/flags.py raw` -v -n eth-raw -d $(BUILD_DIR)/ man
chainlib-man.py -b `PYTHONPATH=. python chainlib/eth/runnable/flags.py wait` -v -n eth-wait -d $(BUILD_DIR)/ man
chainlib-man.py -b `PYTHONPATH=. python chainlib/eth/runnable/flags.py balance` -v -n eth-balance -d $(BUILD_DIR)/ man
.PHONY: man

View File

@ -4,7 +4,7 @@ from hexathon import (
strip_0x,
uniform,
)
from funga.eth.encoding import (
from crypto_dev_signer.encoding import (
is_address,
is_checksum_address,
to_checksum_address,
@ -42,7 +42,3 @@ class AddressChecksum:
:returns: Checksum address
"""
return to_checksum_address(v)
def is_same_address(a, b):
return uniform(strip_0x(a)) == uniform(strip_0x(b))

View File

@ -1,11 +1,10 @@
import sys
# external imports
from chainlib.jsonrpc import JSONRPCRequest
from chainlib.block import Block as BaseBlock
from hexathon import (
add_0x,
strip_0x,
compact,
even,
)
# local imports
@ -35,8 +34,7 @@ def block_by_hash(hsh, include_tx=True, id_generator=None):
def block_by_number(n, include_tx=True, id_generator=None):
"""Implements chainlib.interface.ChainInterface method
"""
hx = strip_0x(hex(n))
nhx = add_0x(compact(hx), compact_value=True)
nhx = add_0x(even(hex(n)[2:]))
j = JSONRPCRequest(id_generator)
o = j.template()
o['method'] = 'eth_getBlockByNumber'
@ -62,20 +60,6 @@ def transaction_count(block_hash, id_generator=None):
return j.finalize(o)
def syncing(id_generator=None):
"""Request the syncing state of the node
:param id_generator: JSONRPC id generator
:type id_generator: JSONRPCIdGenerator
:rtype: dict
:returns: rpc query object
"""
j = JSONRPCRequest(id_generator)
o = j.template()
o['method'] = 'eth_syncing'
return j.finalize(o)
class Block(BaseBlock):
"""Encapsulates an Ethereum block

View File

@ -1,8 +1,22 @@
# standard imports
import os
import logging
# external imports
from chainlib.cli import Rpc as BaseRpc
from chainlib.eth.connection import EthHTTPConnection
from chainlib.cli import (
ArgumentParser,
argflag_std_read,
argflag_std_write,
argflag_std_base,
Config as BaseConfig,
Wallet as BaseWallet,
Rpc as BaseRpc, Flag,
)
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
# local imports
from chainlib.eth.address import AddressChecksum
from chainlib.eth.connection import EthHTTPConnection
from chainlib.eth.gas import (
OverrideGasOracle,
RPCGasOracle,
@ -12,6 +26,20 @@ from chainlib.eth.nonce import (
RPCNonceOracle,
)
logg = logging.getLogger(__name__)
script_dir = os.path.dirname(os.path.realpath(__file__))
class Wallet(BaseWallet):
"""Convenience constructor to set Ethereum defaults for chainlib cli Wallet object
:param checksummer: Address checksummer object
:type checksummer: Implementation of chainlib.eth.address.AddressChecksum
"""
def __init__(self, checksummer=AddressChecksum):
super(Wallet, self).__init__(EIP155Signer, checksummer=checksummer)
# TODO: how is the keystore implemented in rpc here?
class Rpc(BaseRpc):
@ -69,3 +97,27 @@ class Rpc(BaseRpc):
def get_gas_oracle(self):
return self.get_fee_oracle()
class Config(BaseConfig):
"""Convenience constructor to set Ethereum defaults for the chainlib cli config object
"""
default_base_config_dir = os.path.join(script_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'):
config.add('default', 'RPC_DIALECT', exists_ok=True)
elif config.get('RPC_DIALECT') not in [
'openethereum',
'default',
]:
raise ValueError('unknown rpc dialect {}'.format(config.get('RPC_DIALECT')))
return config

View File

@ -1,4 +0,0 @@
from .arg import *
from .config import Config
from .rpc import Rpc
from .wallet import Wallet

View File

@ -1,10 +0,0 @@
# 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,
)

View File

@ -1,33 +0,0 @@
# standard imports
import os
# external imports
from chainlib.cli import Config as BaseConfig
script_dir = os.path.dirname(os.path.realpath(__file__))
data_dir = os.path.join(script_dir, '..')
class Config(BaseConfig):
"""Convenience constructor to set Ethereum defaults for the chainlib cli config object
"""
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'):
config.add('default', 'RPC_DIALECT', exists_ok=True)
elif config.get('RPC_DIALECT') not in [
'openethereum',
'default',
]:
raise ValueError('unknown rpc dialect {}'.format(config.get('RPC_DIALECT')))
return config

View File

@ -1,103 +0,0 @@
# standard imports
import re
import logging
# external imports
from chainlib.eth.contract import (
ABIContractType,
ABIContractEncoder,
)
logg = logging.getLogger(__name__)
class CLIEncoder(ABIContractEncoder):
__re_uint = r'^([uU])[int]*([0-9]+)?$'
__re_bytes = r'^([bB])[ytes]*([0-9]+)?$'
__re_string = r'^([sS])[tring]*$'
__re_address = r'^([aA])[ddress]*?$'
__translations = [
'to_uint',
'to_bytes',
'to_string',
'to_address',
]
def __init__(self, signature=None):
super(CLIEncoder, self).__init__()
self.signature = signature
if signature != None:
self.method(signature)
def to_uint(self, typ):
s = None
a = None
m = re.match(self.__re_uint, typ)
if m == None:
return None
n = m.group(2)
if m.group(2) == None:
n = 256
s = 'UINT256'.format(m.group(2))
a = getattr(ABIContractType, s)
return (s, a)
def to_bytes(self, typ):
s = None
a = None
m = re.match(self.__re_bytes, typ)
if m == None:
return None
n = m.group(2)
if n == None:
n = 32
s = 'BYTES{}'.format(n)
a = getattr(ABIContractType, s)
return (s, a)
def to_address(self, typ):
s = None
a = None
m = re.match(self.__re_address, typ)
if m == None:
return None
s = 'ADDRESS'
a = getattr(ABIContractType, s)
return (s, a)
def to_string(self, typ):
m = re.match(self.__re_string, typ)
if m == None:
return None
s = 'STRING'
a = getattr(ABIContractType, s)
return (s, a)
def translate_type(self, typ):
r = None
for tr in self.__translations:
r = getattr(self, tr)(typ)
if r != None:
break
if r == None:
raise ValueError('no translation for type {}'.format(typ))
logg.debug('type {} translated to {}'.format(typ, r[0]))
return r[1]
def add_from(self, arg):
logg.debug('arg {}'.format(arg))
(typ, val) = arg.split(':', maxsplit=1)
real_typ = self.translate_type(typ)
if self.signature != None:
self.typ(real_typ)
fn = getattr(self, real_typ.value)
fn(val)

View File

@ -1,20 +0,0 @@
# external imports
from funga.eth.signer import EIP155Signer
from funga.eth.keystore.dict import DictKeystore
from chainlib.cli import Wallet as BaseWallet
# local imports
from chainlib.eth.address import AddressChecksum
class Wallet(BaseWallet):
"""Convenience constructor to set Ethereum defaults for chainlib cli Wallet object
:param checksummer: Address checksummer object
:type checksummer: Implementation of chainlib.eth.address.AddressChecksum
"""
def __init__(self, checksummer=AddressChecksum):
super(Wallet, self).__init__(EIP155Signer, checksummer=checksummer, keystore=DictKeystore())

View File

@ -36,7 +36,6 @@ from chainlib.jsonrpc import (
from chainlib.eth.tx import (
unpack,
)
from potaahto.symbols import snake_and_camel
logg = logging.getLogger(__name__)
@ -89,12 +88,7 @@ class EthHTTPConnection(JSONRPCHTTPConnection):
e = jsonrpc_result(r, error_parser)
if e != None:
e = snake_and_camel(e)
# In openethereum we encounter receipts that have NONE block hashes and numbers. WTF...
logg.debug('({}) poll receipt received {}'.format(str(self), r))
if e['block_hash'] == None:
logg.warning('poll receipt attempt {} returned receipt but with a null block hash value!'.format(i))
else:
logg.debug('({}) poll receipt completed {}'.format(str(self), r))
logg.debug('e {}'.format(strip_0x(e['status'])))
if strip_0x(e['status']) == '00':
raise RevertEthException(tx_hash_hex)

View File

@ -1,13 +1,13 @@
[rpc]
http_provider = http://localhost:8545
provider = http://localhost:8545
auth =
credentials =
dialect = default
scheme = http
verify = 1
[chain]
spec = evm:berlin:1:ethereum
spec = evm:ethereum:1
[wallet]
key_file =

View File

@ -1,2 +0,0 @@
[rpc]
provider = Fully-qualified URL to EVM node's RPC endpoint.

View File

@ -6,7 +6,7 @@ from hexathon import (
add_0x,
strip_0x,
)
from funga.eth.transaction import EIP155Transaction
from crypto_dev_signer.eth.transaction import EIP155Transaction
# local imports
from chainlib.fee import FeeOracle
@ -55,7 +55,7 @@ def balance(address, id_generator=None, height=BlockSpec.LATEST):
j = JSONRPCRequest(id_generator)
o = j.template()
o['method'] = 'eth_getBalance'
o['params'].append(add_0x(address))
o['params'].append(address)
height = to_blockheight_param(height)
o['params'].append(height)
return j.finalize(o)

View File

@ -44,7 +44,7 @@ class NonceOracle(BaseNonceOracle):
"""
def __init__(self, address, id_generator=None):
self.id_generator = id_generator
super(NonceOracle, self).__init__(add_0x(address))
super(NonceOracle, self).__init__(address)
def get_nonce(self):

View File

@ -5,8 +5,8 @@ import logging
# external imports
import eth_tester
import pytest
from funga.eth.signer import EIP155Signer
from funga.eth.keystore.dict import DictKeystore
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from crypto_dev_signer.keystore.dict import DictKeystore
# local imports
from chainlib.eth.unittest.base import *

View File

@ -3,6 +3,7 @@
# external imports
import pytest
#from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
@pytest.fixture(scope='function')

View File

@ -24,7 +24,7 @@ from chainlib.eth.gas import (
balance,
)
from chainlib.chain import ChainSpec
from funga.eth.signer import EIP155Signer
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()

View File

@ -15,8 +15,8 @@ from chainlib.eth.connection import EthHTTPConnection
from chainlib.eth.tx import count
from chainlib.chain import ChainSpec
from chainlib.jsonrpc import IntSequenceGenerator
from funga.eth.keystore.dict import DictKeystore
from funga.eth.signer import EIP155Signer
from crypto_dev_signer.keystore.dict import DictKeystore
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from hexathon import add_0x
logging.basicConfig(level=logging.WARNING)
@ -25,7 +25,7 @@ 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
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')
args = argparser.parse_args()
@ -43,7 +43,7 @@ conn = rpc.connect_by_config(config)
def main():
# TODO: should tolerate if address not prefixed with 0x
o = count(add_0x(holder_address), id_generator=rpc.id_generator)
o = count(holder_address, id_generator=rpc.id_generator)
r = conn.do(o)
count_result = None
try:

View File

@ -1,205 +0,0 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# standard imports
import io
import sys
import os
import json
import argparse
import logging
import urllib
import sha3
# external imports
from chainlib.cli import flag_reset
from funga.eth.signer import EIP155Signer
from funga.eth.keystore.dict import DictKeystore
from hexathon import (
add_0x,
strip_0x,
)
# local imports
import chainlib.eth.cli
from chainlib.eth.cli.encode import CLIEncoder
from chainlib.eth.constant import ZERO_ADDRESS
from chainlib.eth.address import to_checksum
from chainlib.eth.connection import EthHTTPConnection
from chainlib.jsonrpc import (
JSONRPCRequest,
IntSequenceGenerator,
)
from chainlib.eth.tx import (
TxFactory,
TxFormat,
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
from chainlib.eth.address import to_checksum_address
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 | 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)
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
wallet = chainlib.eth.cli.Wallet(EIP155Signer)
wallet.from_config(config)
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
conn = rpc.connect_by_config(config)
send = config.true('_RPC_SEND')
chain_spec = None
try:
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
except AttributeError:
pass
def main():
signer_address = ZERO_ADDRESS
signer = None
try:
signer = rpc.get_signer()
signer_address = rpc.get_signer_address()
except SignerMissingException:
pass
code = '0x'
cli_encoder = CLIEncoder(signature=config.get('_SIGNATURE'))
for arg in config.get('_CONTRACT_ARGS'):
cli_encoder.add_from(arg)
code += cli_encoder.get()
exec_address = config.get('_EXEC_ADDRESS')
if exec_address:
exec_address = add_0x(to_checksum_address(exec_address))
mode = args.mode
if mode == None:
if signer == None:
mode = 'call'
else:
mode = 'tx'
if not config.get('_SIGNATURE'):
if mode != 'arg':
logg.error('mode tx without contract method signature makes no sense. Use eth-get with --data instead.')
sys.exit(1)
if args.format == 'rpc':
logg.error('rpc format with arg put does not make sense')
sys.exit(1)
if mode == 'arg':
print(strip_0x(code))
return
elif not exec_address:
logg.error('exec address (-e) must be defined with mode "{}"'.format(args.mode))
sys.exit(1)
if config.get('RPC_PROVIDER'):
logg.debug('provider {}'.format(config.get('RPC_PROVIDER')))
if not config.get('_FEE_LIMIT') or not config.get('_FEE_PRICE'):
gas_oracle = rpc.get_gas_oracle()
(price, limit) = gas_oracle.get_gas()
if not config.get('_FEE_PRICE'):
config.add(price, '_FEE_PRICE')
if not config.get('_FEE_LIMIT'):
config.add(limit, '_FEE_LIMIT')
if mode == 'tx':
if not config.get('_NONCE'):
nonce_oracle = rpc.get_nonce_oracle()
config.add(nonce_oracle.get_nonce(), '_NONCE')
else:
for arg in [
'_FEE_PRICE',
'_FEE_LIMIT',
'_NONCE',
]:
if not config.get(arg):
logg.error('--{} must be specified when no rpc provider has been set.'.format(arg.replace('_', '-').lower()))
sys.exit(1)
if mode == 'call': #signer == None or config.true('_NOTX'):
c = TxFactory(chain_spec)
j = JSONRPCRequest(id_generator=rpc.id_generator)
o = j.template()
gas_limit = add_0x(int.to_bytes(config.get('_FEE_LIMIT'), 8, byteorder='big').hex(), compact_value=True)
gas_price = add_0x(int.to_bytes(config.get('_FEE_PRICE'), 8, byteorder='big').hex(), compact_value=True)
o['method'] = 'eth_call'
o['params'].append({
'to': exec_address,
'from': signer_address,
'value': '0x0',
'gas': gas_limit, # TODO: better get of network gas limit
'gasPrice': gas_price,
'data': add_0x(code),
})
height = to_blockheight_param(config.get('_HEIGHT'))
o['params'].append(height)
o = j.finalize(o)
if config.get('_RPC_SEND'):
r = conn.do(o)
try:
print(strip_0x(r))
return
except ValueError:
sys.stderr.write('query returned an empty value ({})\n'.format(r))
sys.exit(1)
else:
print(o)
return
if signer == None:
logg.error('mode "tx" without signer does not make sense. Please specify a key file with -y.')
sys.exit(1)
if chain_spec == None:
raise ValueError('chain spec must be specified')
c = TxFactory(chain_spec, signer=signer, gas_oracle=rpc.get_gas_oracle(), nonce_oracle=rpc.get_nonce_oracle())
tx = c.template(signer_address, config.get('_EXEC_ADDRESS'), use_nonce=True)
tx = c.set_code(tx, code)
tx_format = TxFormat.JSONRPC
if config.get('_RAW'):
tx_format = TxFormat.RLP_SIGNED
(tx_hash_hex, o) = c.finalize(tx, tx_format=tx_format)
if send:
r = conn.do(o)
print(r)
else:
if config.get('_RAW'):
o = strip_0x(o)
print(o)
if __name__ == '__main__':
main()

View File

@ -1,23 +0,0 @@
# standard imports
import sys
# external imports
import chainlib.eth.cli
from hexathon import add_0x
cmds = {
'gas': chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.WALLET,
'info': chainlib.eth.cli.argflag_reset(chainlib.cli.argflag_std_base_read, chainlib.eth.cli.Flag.CHAIN_SPEC),
'get': chainlib.eth.cli.argflag_reset(chainlib.cli.argflag_std_base_read, chainlib.eth.cli.Flag.CHAIN_SPEC),
'decode': chainlib.cli.argflag_std_base | chainlib.eth.cli.Flag.CHAIN_SPEC,
'encode': 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,
'count': chainlib.eth.cli.argflag_std_base_read | chainlib.eth.cli.Flag.WALLET,
'raw': chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC,
'balance': chainlib.eth.cli.argflag_std_base | chainlib.eth.cli.Flag.WALLET,
'wait': chainlib.eth.cli.argflag_reset(chainlib.eth.cli.argflag_std_base_read | chainlib.eth.cli.Flag.NO_TARGET | chainlib.eth.cli.Flag.RPC_AUTH, chainlib.eth.cli.Flag.CHAIN_SPEC | chainlib.eth.cli.Flag.RAW),
'checksum': 0,
}
if __name__ == '__main__':
b = cmds[sys.argv[1]]
print(add_0x(hex(b)))

View File

@ -26,10 +26,6 @@ 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
logging.basicConfig(level=logging.WARNING)
@ -66,12 +62,8 @@ send = config.true('_RPC_SEND')
def balance(address, id_generator):
o = gas_balance(address, id_generator=id_generator)
r = conn.do(o)
try:
balance = int(r)
except ValueError:
balance = strip_0x(r)
balance = int(balance, 16)
return balance
hx = strip_0x(r)
return int(hx, 16)
def main():
@ -81,32 +73,32 @@ def main():
g = Gas(chain_spec, signer=signer, gas_oracle=rpc.get_gas_oracle(), nonce_oracle=rpc.get_nonce_oracle())
recipient = to_checksum_address(config.get('_RECIPIENT'))
if not config.true('_UNSAFE') and not is_checksum_address(recipient):
if not config.true('_UNSAFE') and recipient != add_0x(config.get('_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)
sender_balance = balance(signer_address, rpc.id_generator)
recipient_balance = balance(recipient, rpc.id_generator)
logg.debug('sender {} balance before: {}'.format(signer_address, sender_balance))
logg.debug('recipient {} balance before: {}'.format(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(signer_address, recipient, value, data=config.get('_DATA'), id_generator=rpc.id_generator)
if send:
conn.do(o)
if block_last:
r = 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)
sender_balance = balance(signer_address, rpc.id_generator)
recipient_balance = balance(recipient, rpc.id_generator)
logg.debug('sender {} balance after: {}'.format(signer_address, sender_balance))
logg.debug('recipient {} balance after: {}'.format(recipient, recipient_balance))
if r['status'] == 0:
logg.critical('VM revert for {}. Wish I could tell you more'.format(tx_hash_hex))
logg.critical('VM revert. Wish I could tell you more')
sys.exit(1)
print(tx_hash_hex)
else:

View File

@ -48,8 +48,7 @@ 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)
arg_flags = chainlib.eth.cli.argflag_std_read
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_positional('item', type=str, help='Address or transaction to retrieve data for')
args = argparser.parse_args()
@ -64,7 +63,6 @@ item = add_0x(args.item)
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'
@ -99,13 +97,13 @@ def get_transaction(conn, tx_hash, id_generator):
r = conn.do(o)
block = Block(r)
tx.apply_block(block)
logg.debug('foo {}'.format(tx_src))
tx.generate_wire(chain_spec)
return tx
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'

View File

@ -16,7 +16,7 @@ from hexathon import (
even,
)
import sha3
from funga.eth.signer import EIP155Signer
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
# local imports
from chainlib.eth.address import AddressChecksum
@ -24,7 +24,6 @@ from chainlib.eth.chain import network_id
from chainlib.eth.block import (
block_latest,
block_by_number,
syncing,
Block,
)
from chainlib.eth.tx import count
@ -44,35 +43,21 @@ logg = logging.getLogger()
script_dir = os.path.dirname(os.path.realpath(__file__))
config_dir = os.path.join(script_dir, '..', 'data', 'config')
results_translation = {
'network_id': 'Network Id',
'block': 'Block',
'syncing': 'Syncing',
'gas_limit': 'Gas Limit',
'gas_price': 'Gas Price',
'block_time': 'Block time',
}
arg_flags = chainlib.eth.cli.argflag_std_read
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_positional('address', type=str, help='Address to retrieve info for', required=False)
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')
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)
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args={'long': None}, default_config_dir=config_dir)
if config.get('_ENTRY') != None:
if config.get('_ENTRY') not in results_translation.keys():
raise ValueError('Unknown entry {}'.format(config.get('_ENTRY')))
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)
rpc = chainlib.eth.cli.Rpc()
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
conn = rpc.connect_by_config(config)
token_symbol = 'eth'
@ -83,37 +68,20 @@ human = not config.true('_RAW')
longmode = config.true('_LONG')
def set_result(results, k, v, w=sys.stdout):
kt = results_translation[k]
if str(config.get('_ENTRY')) == k:
w.write('{}'.format(v))
return True
logg.info('{}: {}\n'.format(kt, v))
results[k] = v
return False
def main():
results = {}
o = network_id(id_generator=rpc.id_generator)
r = conn.do(o)
#if human:
# n = format(n, ',')
if set_result(results, 'network_id', r):
return
sys.stdout.write('Network id: {}\n'.format(r))
o = block_latest(id_generator=rpc.id_generator)
r = conn.do(o)
try:
n = int(r, 16)
except ValueError:
n = int(r)
first_block_number = n
if human:
n = format(n, ',')
if set_result(results, 'block', n):
return
sys.stdout.write('Block: {}\n'.format(n))
o = block_by_number(first_block_number, False, id_generator=rpc.id_generator)
r = conn.do(o)
@ -139,31 +107,22 @@ def main():
if human:
n = format(n, ',')
if set_result(results, 'gas_limit', n):
return
if set_result(results, 'block_time', aggr_time / BLOCK_SAMPLES):
return
sys.stdout.write('Gaslimit: {}\n'.format(n))
sys.stdout.write('Blocktime: {}\n'.format(aggr_time / BLOCK_SAMPLES))
o = price(id_generator=rpc.id_generator)
r = conn.do(o)
n = int(r, 16)
if human:
n = format(n, ',')
if set_result(results, 'gas_price', n):
return
sys.stdout.write('Gasprice: {}\n'.format(n))
if config.get('_LOCAL'):
o = syncing()
if holder_address != None:
o = count(holder_address)
r = conn.do(o)
if set_result(results, 'syncing', r):
return
if config.get('_ENTRY') != None:
raise RuntimeError('entry {} ({}) not processed, please review the flag settings'.format(config.get('_ENTRY'), results_translation[config.get('_ENTRY')]))
for k in results.keys():
kt = results_translation[k]
sys.stdout.write('{}: {}\n'.format(kt, results[k]))
n = int(r, 16)
sys.stdout.write('Address: {}\n'.format(holder_address))
sys.stdout.write('Nonce: {}\n'.format(n))
if __name__ == '__main__':

View File

@ -11,8 +11,8 @@ import urllib
# external imports
import chainlib.eth.cli
from funga.eth.signer import EIP155Signer
from funga.eth.keystore.dict import DictKeystore
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from crypto_dev_signer.keystore.dict import DictKeystore
from hexathon import (
add_0x,
strip_0x,
@ -50,8 +50,6 @@ 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)
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')
args = argparser.parse_args()
config = chainlib.eth.cli.Config.from_args(args, arg_flags, default_config_dir=config_dir)
@ -67,6 +65,9 @@ conn = rpc.connect_by_config(config)
send = config.true('_RPC_SEND')
if config.get('_EXEC_ADDRESS') != None:
send = False
chain_spec = None
try:
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
@ -82,15 +83,11 @@ def main():
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:
if not args.u and exec_address != add_0x(exec_address):
raise ValueError('invalid checksum address')
if signer_address == None:
j = JSONRPCRequest(id_generator=rpc.id_generator)
o = j.template()
o['method'] = 'eth_call'
@ -109,10 +106,11 @@ def main():
try:
print(strip_0x(r))
except ValueError:
sys.stderr.write('query returned an empty value ({})\n'.format(r))
sys.stderr.write('query returned an empty value\n')
sys.exit(1)
return
else:
if signer_address != None:
if 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())
@ -126,19 +124,13 @@ def main():
r = conn.do(o)
print(r)
else:
if config.get('_RAW'):
o = strip_0x(o)
print(o)
print(tx_hash_hex)
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)
if r['status'] == 0:
logg.critical('VM revert for {}. Wish I could tell you more'.format(tx_hash_hex))
sys.exit(1)
print(r)
else:
print(o)

View File

@ -1,114 +0,0 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# standard imports
import io
import sys
import os
import json
import argparse
import logging
import urllib
# external imports
import chainlib.eth.cli
from funga.eth.signer import EIP155Signer
from funga.eth.keystore.dict import DictKeystore
from hexathon import (
add_0x,
strip_0x,
uniform as hex_uniform,
)
# 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,
)
from chainlib.eth.gas import (
RPCGasOracle,
OverrideGasOracle,
)
from chainlib.eth.tx import (
TxFactory,
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
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_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')
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)
chain_spec = None
try:
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
except AttributeError:
pass
def main():
hashes_ready = []
hashes_ignore = []
for hsh in config.get('_IGNORE'):
hashes_ignore.append(add_0x(hex_uniform(strip_0x(hsh))))
if len(config.get('_HASHES')) == 1:
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])
for hsh in f:
logg.debug('hshs {}'.format(hsh))
hashes_ready.append(add_0x(hex_uniform(strip_0x(hsh.rstrip()))))
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 hashes_ready:
logg.debug('processing transaction hash {}'.format(hsh))
try:
r = conn.wait(hsh)
except RevertEthException:
if config.get('_IGNORE_ALL') or hsh in hashes_ignore:
logg.info('ignoring revert in transaction hash {}'.format(hsh))
continue
sys.stderr.write('revert in transaction hash {}\n'.format(hsh))
sys.exit(1)
if __name__ == '__main__':
main()

View File

@ -13,11 +13,9 @@ from hexathon import (
)
from rlp import decode as rlp_decode
from rlp import encode as rlp_encode
from funga.eth.transaction import EIP155Transaction
from funga.eth.encoding import (
public_key_to_address,
chain_id_to_v,
)
from crypto_dev_signer.eth.transaction import EIP155Transaction
from crypto_dev_signer.encoding import public_key_to_address
from crypto_dev_signer.eth.encoding import chain_id_to_v
from potaahto.symbols import snake_and_camel
from chainlib.hash import keccak256_hex_to_hex
from chainlib.status import Status
@ -95,11 +93,7 @@ def pack(tx_src, chain_spec):
tx_src['r'],
tx_src['s'],
]:
try:
a = strip_0x(a)
except TypeError:
a = strip_0x(hex(a)) # believe it or not, eth_tester returns signatures as ints not hex
for b in bytes.fromhex(a):
for b in bytes.fromhex(strip_0x(a)):
signature[cursor] = b
cursor += 1
@ -166,7 +160,6 @@ def __unpack_raw(tx_raw_bytes, chain_id=1):
vb = chain_id
if chain_id != 0:
v = int.from_bytes(d[6], 'big')
if v > 29:
vb = v - (chain_id * 2) - 35
r = bytearray(32)
r[32-len(d[7]):] = d[7]
@ -174,6 +167,7 @@ def __unpack_raw(tx_raw_bytes, chain_id=1):
s[32-len(d[8]):] = d[8]
logg.debug('vb {}'.format(vb))
sig = b''.join([r, s, bytes([vb])])
#so = KeyAPI.Signature(signature_bytes=sig)
h = sha3.keccak_256()
h.update(rlp_encode(d))
@ -305,7 +299,7 @@ class TxFactory:
:param chain_spec: Chain spec to use for signer.
:type chain_spec: chainlib.chain.ChainSpec
:param signer: Signer middleware.
:type param: Object implementing interface ofchainlib.eth.connection.sign_transaction_to_wire
:type param: Object implementing interface of chainlib.eth.connection.sign_transaction_to_wire
:param gas_oracle: Backend to generate gas parameters
:type gas_oracle: Object implementing chainlib.eth.gas.GasOracle interface
:param nonce_oracle: Backend to generate gas parameters
@ -401,13 +395,11 @@ class TxFactory:
"""
txe = EIP155Transaction(tx, tx['nonce'], tx['chainId'])
txes = txe.serialize()
gas_price = strip_0x(txes['gasPrice'])
gas = strip_0x(txes['gas'])
return {
'from': tx['from'],
'to': txes['to'],
'gasPrice': add_0x(compact(gas_price)),
'gas': add_0x(compact(gas)),
'gasPrice': '0x' + compact(txes['gasPrice']),
'gas': '0x' + compact(txes['gas']),
'data': txes['data'],
}
@ -428,9 +420,7 @@ class TxFactory:
elif tx_format == TxFormat.RLP_SIGNED:
return self.build_raw(tx)
elif tx_format == TxFormat.RAW_ARGS:
return strip_0x(tx['data'])
elif tx_format == TxFormat.DICT:
return tx
return tx['data']
raise NotImplementedError('tx formatting {} not implemented'.format(tx_format))
@ -527,7 +517,7 @@ class Tx(BaseTx):
#:todo: divide up constructor method
"""
def __init__(self, src, block=None, rcpt=None, strict=False):
def __init__(self, src, block=None, rcpt=None):
self.__rcpt_block_hash = None
src = self.src_normalize(src)
@ -556,9 +546,6 @@ class Tx(BaseTx):
self.outputs = [to_checksum(address_from)]
self.contract = None
self.fee_limit = self.gas_limit
self.fee_price = self.gas_price
try:
inpt = src['input']
except KeyError:
@ -580,14 +567,13 @@ class Tx(BaseTx):
try:
self.wire = src['raw']
except KeyError:
logg.debug('no inline raw tx src, and no raw rendering implemented, field will be "None"')
logg.warning('no inline raw tx src, and no raw rendering implemented, field will be "None"')
self.status = Status.PENDING
self.logs = None
self.tx_rcpt_src = None
if rcpt != None:
self.apply_receipt(rcpt, strict=strict)
self.apply_receipt(rcpt)
self.v = src.get('v')
self.r = src.get('r')
@ -630,11 +616,7 @@ class Tx(BaseTx):
return self.src()
def rcpt_src(self):
return self.tx_rcpt_src
def apply_receipt(self, rcpt, strict=False):
def apply_receipt(self, rcpt):
"""Apply receipt data to transaction object.
Effect is the same as passing a receipt at construction.
@ -644,7 +626,6 @@ class Tx(BaseTx):
"""
rcpt = self.src_normalize(rcpt)
logg.debug('rcpt {}'.format(rcpt))
self.tx_rcpt_src = rcpt
tx_hash = add_0x(rcpt['transaction_hash'])
if rcpt['transaction_hash'] != add_0x(self.hash):
@ -659,12 +640,6 @@ class Tx(BaseTx):
status_number = int(rcpt['status'], 16)
except TypeError:
status_number = int(rcpt['status'])
except KeyError as e:
if strict:
raise(e)
logg.debug('setting "success" status on missing status property for {}'.format(self.hash))
status_number = 1
if rcpt['block_number'] == None:
self.status = Status.PENDING
else:
@ -719,12 +694,12 @@ class Tx(BaseTx):
@staticmethod
def from_src(src, block=None, rcpt=None, strict=False):
def from_src(src, block=None, rcpt=None):
"""Creates a new Tx object.
Alias of constructor.
"""
return Tx(src, block=block, rcpt=rcpt, strict=strict)
return Tx(src, block=block, rcpt=rcpt)
def __str__(self):

View File

@ -25,8 +25,8 @@ from hexathon import (
)
from chainlib.eth.tx import receipt
from funga.eth.signer import EIP155Signer
from funga.eth.encoding import private_key_to_address
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from crypto_dev_signer.encoding import private_key_to_address
logg = logging.getLogger().getChild(__name__)

View File

@ -5,8 +5,8 @@ import logging
# external imports
import eth_tester
from funga.eth.signer import EIP155Signer
from funga.eth.keystore.dict import DictKeystore
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from crypto_dev_signer.keystore.dict import DictKeystore
from hexathon import (
strip_0x,
add_0x,
@ -26,8 +26,7 @@ from chainlib.connection import (
from chainlib.eth.address import to_checksum_address
from chainlib.chain import ChainSpec
#logg = logging.getLogger(__name__)
logg = logging.getLogger()
logg = logging.getLogger(__name__)
test_address = bytes.fromhex('Eb3907eCad74a0013c259D5874AE7f22DcBcC95C')
@ -64,7 +63,6 @@ class EthTesterCase(unittest.TestCase):
self.helper = eth_tester_instance
self.backend = self.helper.backend
self.rpc = TestRPCConnection(None, eth_tester_instance, self.signer)
for a in self.keystore.list():
self.accounts.append(add_0x(to_checksum_address(a)))
@ -77,5 +75,6 @@ class EthTesterCase(unittest.TestCase):
RPCConnection.register_location('custom', self.chain_spec, tag='signer', exist_ok=True)
def tearDown(self):
pass

View File

@ -1,16 +0,0 @@
.TH eth-balance 1
.SH NAME
eth-balance \- Get network gas token balance for an address
.SH SYNOPSIS
\fBeth-balance\fP [ -p \fIrpc_provider\fP ] \fIaddress\fP
.SH DESCRIPTION
.P
Query the network for the gas token balance of an account.
.P
The balance will be returned as an integer value denominated in the gas unit.
.SS OPTION

View File

@ -1 +0,0 @@
a Address to get balance for. -a, --address

View File

@ -1,7 +0,0 @@
.SH ABOUT THE CHECKSUM
.P
Checksum protection may be used as protection against address typos.
.P
It uses the value of the address itself as input to an algorithm, which results in altering the case of the letters in the hexadecimal address.
.P
A checksum address consisting of only numbers will always pass checksum.

View File

@ -1,11 +0,0 @@
.TH eth-checksum 1
.SH NAME
eth-checksum \- Calculate checksum address
.SH SYNOPSIS
\fBeth-checksum\fP \fIaddress\fP
.SH DESCRIPTION
.P
Converts \fIaddress\fP into a checksum-protected address.

View File

@ -1,15 +0,0 @@
.TH eth-count 1
.SH NAME
eth-count \- Get transaction count for an address
.SH SYNOPSIS
\fBeth-count\fP [ -p \fIrpc_provider\fP ] \fIaddress\fP
.SH DESCRIPTION
.P
Query the network for the number of transactions known for an account. The result can be used as the \fInonce\fP value for a consecutive transaction.
.SS OPTIONS

View File

@ -1 +0,0 @@
a Address to count transactions for. -a, --address

View File

@ -1,14 +0,0 @@
.TH eth-decode 1
.SH NAME
eth-decode \- Decode a transaction in wire-format to human readable form
.SH SYNOPSIS
\fBeth-decode\fI [ -i \fIchain_spec\fP ] encoded_tx
.SH DESCRIPTION
.P
This tool deserializes a wire-format transaction and outputs its fields in human-readable form. It does not require a node to operate, and does not apply transaction state.
.P
The transaction wire-format is an ordered concatenation of transaction values, which in turn is serialized using the Recurive Length Prefix (RLP) format. \fBeth-decode\fP accepts the RLP-encoded transaction as a hex string.
.P

View File

@ -1,31 +0,0 @@
.SH MODES
The modes parameter specifies what kind of operaion the encoding should be generated for. There are three valid modes:
.SS tx
Generates a transaction that changes state. Used with \fB--format\fP \fIrpc\fP it will generate an \fIeth_sendRawTransaction\fP jsonrpc object. Used with \fB--format\fP \fIbin\fP it outputs signed RLP only in hexdecimal.
.SS call
Generates a query that reads state. Used with \fB--format\fP \fIrpc\fP it will generate a \fIeth_call\fP jsonrpc object with applicable fields filled out from environment, arguments and/or rpc retrieval. \fB--format\fP \fIbin\fP is not valid for this mode.
.SS arg
Encodes the argument part only, optionally with a method signature. \fB--format\fP \fIrpc\fP is not valid for this mode.
.SH SPECIFYING VALUES
Dynamic value types are not yet supported.
.SS Specifying an unsigned integer:
.IP u:1024
.SS Specifying an address:
.IP a:19062190B1925b5b6689D7073fDfC8c2976EF8Cb
.SS Specifying bytes values:
.IP b:deadbeef
.IP b4:deadbeef
.IP b32:deadbeef
.SS Specifying a string value:
.IP s:foobar

View File

@ -1,22 +0,0 @@
.SS Build a signed ERC20 transfer in wire format, setting nonce and fee details manually.
.EX
$ eth-encode -f bin -y <\fIkey_file_path\fP> -e <\fItoken_address\fP> --fee-price 1000000000 --fee-limit 100000 --nonce 42 --signature transfer a:00000000000000000000000000000000DeaDBeef u:1024
.EE
.SS Build Smart contract call with method signature, retrieving fee and nonce settings from rpc
\fBeth-encode\fP --mode call -f rpc -e <\fItoken_address\fP> --signature balanceOf -p <\fIrpc_endpoint\fP> a:deadbeef
.SS Build smart contract ERC20 transfer argument with signature
.EX
\fBeth-encode\fP --mode arg --signature transfer a:00000000000000000000000000000000DeaDBeef u:1024
.TP Outupt
a9059cbb00000000000000000000000000000000000000000000000000000000deadbeef0000000000000000000000000000000000000000000000000000000000000400
.EE
.SS Build smart contract ERC20 transfer argument types without signature
.EX
\fBeth-encode\fP --mode arg a:00000000000000000000000000000000DeaDBeef u:1024
.TP Output
00000000000000000000000000000000000000000000000000000000deadbeef0000000000000000000000000000000000000000000000000000000000000400
.EE

View File

@ -1,17 +0,0 @@
.TH eth-encode 1
.SH NAME
eth-encode \- Encode arbitrary contract calls and transactions
.SH SYNOPSIS
\fBeth-encode\fP [ --mode \fImode\fP ] [ -f \fIformat\fP ] [ -p \fIrpc_provider\fP ] [ -i \fIchain_Spec\fP] [ -s ] [ -w ] [ -e \fIsmart_contract_address\fP ] --signature \fIcontract_method_name\fP [ \fIvalue_specifiers\fP ... ]
.SH DESCRIPTION
Generate wire-format or rpc query any EVM smart contract call or transaction by specifying method and arguments.
The resulting call or transaction can either be stored offline for later use, or directly sent to the network using the \fB-s\fP option.
A description of value formatting is given in the \fBSPECIFYING VALUES\fP section below. Usage is demonstrated in the \fBEXAMPLES\fP section.
.SS OPTIONS

View File

@ -1 +0,0 @@
mode Mode of operation encoding is for. Must be one of "tx," "call," or "arg." See \fBMODES\fP below. --mode mode

View File

@ -1,75 +0,0 @@
.P
In the follwing we willconsaider transactions signed by the private key for address Eb3907eCad74a0013c259D5874AE7f22DcBcC95C:
.SS Offline transaction
.EX
$ eth-gas -y <\fIkey_file_path\fP> -a 00000000000000000000000000000000DeaDBeef --fee-price 100000000000 --fee-limit 21000 --nonce 42 1024
from: Eb3907eCad74a0013c259D5874AE7f22DcBcC95C
to: 00000000000000000000000000000000DeaDBeef
nonce: 42
gasPrice: 100000000000 (100 gwei)
gas: 21000
value: 1024 (0.000000000000001024 eth)
data: 0x
v: 37
recovery_byte: 0
r: 0x0c97432d4db724e66a56f7ced04174cf6129e2555709f206dd6d3a156b4af23a
s: 0x287862548314a59c7ca6139eee8b51400eb40a67b08b8dc13d67302abecccae0
chainId: 1
hash: 0x003030af05460633e85b16fff7a17607818dc67e58f89396e5491ad6f5438971
hash_unsigned: 0xa59cf9e5438b186de381892b7879ce66476d5469478c7148880da5d553ade651
src: 0xf8662a85174876e8008252089400000000000000000000000000000000deadbeef8204008025a00c97432d4db724e66a56f7ced04174cf6129e2555709f206dd6d3a156b4af23aa0287862548314a59c7ca6139eee8b51400eb40a67b08b8dc13d67302abecccae0
.EE
.SS Offline transaction with arbitrary data
.EX
$ eth-gas -y <\fIkey_file_path\fP> -a 00000000000000000000000000000000DeaDBeef --fee-price 100000000000 --fee-limit 21000 --nonce 42 --data 0x2a 1024
from: Eb3907eCad74a0013c259D5874AE7f22DcBcC95C
to: 00000000000000000000000000000000DeaDBeef
nonce: 42
gasPrice: 100000000000 (100 gwei)
gas: 21000
value: 1024 (0.000000000000001024 eth)
data: 0x2a
v: 37
recovery_byte: 0
r: 0x800b6982d3f178201d7e7e7693b9c90b3fbcd54d04b6fff5284c81101fad54dd
s: 0x3b86d710d31ac74b58f0040b0f51fdb6bdbabea62a68cf99c05e765e7e81de87
chainId: 1
hash: 0xede30052befd80760c5ab543babdccc3d97fe90523e5710d77220155a82faa47
hash_unsigned: 0xad82d8cf1a412541c8a94ef71c50e9172c3a37853af036adee2f55c577da9770
src: 0xf8662a85174876e8008252089400000000000000000000000000000000deadbeef8204002a25a0800b6982d3f178201d7e7e7693b9c90b3fbcd54d04b6fff5284c81101fad54dda03b86d710d31ac74b58f0040b0f51fdb6bdbabea62a68cf99c05e765e7e81de87
.EE
.SS Offline transaction with wire-format output
.EX
$ eth-gas -y <\fIkey_file_path\fP> -a 00000000000000000000000000000000DeaDBeef --fee-price 100000000000 --fee-limit 21000 --nonce 42 --raw 1024
0xf8662a85174876e8008252089400000000000000000000000000000000deadbeef8204008025a00c97432d4db724e66a56f7ced04174cf6129e2555709f206dd6d3a156b4af23aa0287862548314a59c7ca6139eee8b51400eb40a67b08b8dc13d67302abecccae0
.EE
.SS Sign transaction for a different network
.EX
$ eth-gas -i evm:london:3:rinkeby -y /home/lash/src/contrib/grassrootseconomics/cic-dev/UTC--2021-01-08T17-18-44.521011372Z--eb3907ecad74a0013c259d5874ae7f22dcbcc95c -a 00000000000000000000000000000000DeaDBeef --fee-price 100000000000 --fee-limit 21000 --nonce 42 --data 0x2a 1024
from: Eb3907eCad74a0013c259D5874AE7f22DcBcC95C
to: 00000000000000000000000000000000DeaDBeef
nonce: 42
gasPrice: 100000000000 (100 gwei)
gas: 21000
value: 1024 (0.000000000000001024 eth)
data: 0x2a
v: 41
recovery_byte: 0
r: 0xe522c25784111a512cbf46f883e3bdacffc2cdbd465fa1042892c28fc10ee054
s: 0x5f84eb51a0c9871cfcedaba4e6274b300014899b3a2fec9292de6fe5919bcd07
chainId: 3
hash: 0x771200a20072294a6a843b89b98d5f868c9aa94da75dacf6a9f5834dfd241199
hash_unsigned: 0xa5f9e8a5cda6985d81f4129955b7529f48ecb54728badac16ec550384e3a2bcc
src: 0xf8662a85174876e8008252089400000000000000000000000000000000deadbeef8204002a29a0e522c25784111a512cbf46f883e3bdacffc2cdbd465fa1042892c28fc10ee054a05f84eb51a0c9871cfcedaba4e6274b300014899b3a2fec9292de6fe5919bcd07
.EE
.P
The wire-format can in turn be decoded using the \fBeth-decode\fP tool)

View File

@ -1,19 +0,0 @@
.TH eth-gas 1
.SH NAME
eth-gas - Create EVM gas token transaction
.SH SYNOPSIS
.P
\fBeth-gas\fP -a RECIPIENT -y KEYFILE [ -p \fIrpc_provider ] [ -s ] [ -u ] AMOUNT
.P
\fBeth-gas\fP -a RECIPIENT -y KEYFILE [ --fee-price PRICE ] [ --fee-limit LIMIT ] [ --nonce NONCE ] [ -s ] [ -u ] AMOUNT
.SH DESCRIPTION
\fBeth-gas\fP generates serialized gas token transactions in various output formats.
If an \fIrpc_provider\fP is available, the valuess \fIfee_price\fP and \fInonce\fP will be automatically retrieved if they are not explicitly specified. If missing values cannot be retrieved, an error will occur.
Providing all arguments enables fully offline creation of the transaction.
See \fBEXAMPLES\fP for more details on inputs and outputs.
.SS OPTIONS

View File

@ -1 +0,0 @@
a Beneficiary of the gas token transaction.

View File

@ -1,19 +0,0 @@
.TH eth-get 1
.SH NAME
eth-get \- Retrieve transaction and transaction state from network
.SH SYNOPSIS
.PP
\fBeth-get\fP [ -p \fIrpc_provider\fP] \fItx_hash\fP
.PP
\fBeth-get\fP [ -p \fIrpc_provider\fP] \fIaddress\fP
.SH DESCRIPTION
.P
Retrieve a transaction by its transaction hash, or contract code by its address.
.P
In the context of transaction, \fBeth-get\fP retrieves the state of the transaction (the "receipt") and applies it to the output. It also re-serializes the transaction wire format for reference.
.SS OPTIONS

View File

@ -1,27 +0,0 @@
.TH eth-info 1
.SH NAME
eth-info - Return key metrics from the current state of the EVM network.
.SH SYNOPSIS
\fBeth-info\fP [ -p \fIrpc_provider\fP ] [ \fIkey\fP ]
.SH DESCRIPTION
\fBeth-info\fP reports statistics from the current state of the EVM network:
.EX
$ eth-info
Block: 1024478
Gas price: 1000000000
.EE
An individual data item may be specified using the \fIkey\fP positional argument:
.EX
$ eth-info block
Block: 1024478
$ eth-info --raw gas_price
1000000000
.EE
.SS OPTIONS

View File

@ -1,8 +0,0 @@
.SH EXAMPLES
Outputs of \fBeth-gas\fP and \fBeth-encode\fP map directly to inputs for \fBeth-raw\fP.
.SS Encapsulate a gas transaction
.EX
$ eth-gas -y <\fIkey_file_path\fP> -a 00000000000000000000000000000000DeaDBeef --fee-price 100000000000 --fee-limit 21000 --nonce 42 --raw 1024 | eth-raw

View File

@ -1,26 +0,0 @@
.TH eth-raw 1
.SH NAME
eth-raw \- Create and send a jsonrpc transaction from signed rlp transaction
.SH SYNOPSIS
.P
\fBeth-raw\fP [ --deploy ] \fItransaction_data\fP
.P
\fBeth-raw\fP [ -p \fIrpc_provider\fP ] [ --deploy ] -s \fItransaction_data\fP
.SH DESCRIPTION
.P
Creates a jsonrpc object from serialized data.
.P
If \fB-y\fP is defined, and \fB--mode\fP is not set, or set to "tx", a \fIeth_sendRawTransaction\fP object will be created.
.P
Otherwise, an \fIeth_call\fP object will be created. The recipient of the call will be the contract specified with \fB-e\fP. If \fB-y\fP has been set, the sender of the query will be the address corresponding to the key file.
.P
If the \fB-s\fP option is added, the jsonrpc object will be sent to the rpc provider. \fBNote\fP If the jsonrpc object is a transaction, this may incur real costs.
.P
See the \fBEXAMPLES\fP section for details on how to use \fBeth-raw\fP with the outputs from \fBeth-gas\fP and \fBeth-encode\fP
.SS OPTIONS

View File

@ -1,4 +0,0 @@
.SH SEE ALSO
.BP
confini-dump(1), eth-keyfile(1), eth-encode(1), eth-gas(1)

View File

@ -1,17 +0,0 @@
.TH eth-wait 1
.SH NAE
eth-wait \- Wait for a transaction to be confirmed on network
.SH SYNOPSIS
\fBeth-wait\fP [ -p \fIrpc_provider\fP ] \fItransaction_hash\fP ...
.SH DESCRIPTION
.P
Blocks until network confirmation for the transactions are returned from \fIrpc_provider\fP.
.P
By default, if execution of the transaction failed on the network, the command will cause an error. This behavior can be adjusted using the \fB--ignore\fP or \fB--ignore-all\fP option.
.P
A transaction hash unknown to the \fIrpc_provider\fP will also cause an error.
.SS OPTIONS

View File

@ -1,2 +0,0 @@
ignore Ignore error from the specified transaction. May be defined more than once. --ignore tx_hash
ignoreall Ignore errors from all transactions. --ignore-all

View File

@ -1,7 +1,7 @@
funga-eth~=0.6.0
funga-eth>=0.5.0a1,<0.6.0
pysha3==1.0.2
hexathon~=0.1.5
hexathon~=0.0.1a8
websocket-client==0.57.0
potaahto~=0.1.1
chainlib~=0.1.0
confini~=0.6.0
potaahto~=0.0.1a1
chainlib==0.0.10a1
confini>=0.4.1a1,<0.5.0

View File

@ -1,6 +1,6 @@
[metadata]
name = chainlib-eth
version = 0.1.1
version = 0.0.10a2
description = Ethereum implementation of the chainlib interface
author = Louis Holbrook
author_email = dev@holbrook.no
@ -14,7 +14,6 @@ classifiers =
Programming Language :: Python :: 3
Operating System :: OS Independent
Development Status :: 3 - Alpha
Topic :: Software Development :: Libraries
Environment :: Console
Intended Audience :: Developers
License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
@ -33,18 +32,15 @@ packages =
chainlib.eth.runnable
chainlib.eth.pytest
chainlib.eth.unittest
chainlib.eth.cli
[options.entry_points]
console_scripts =
eth-count = chainlib.eth.runnable.count:main
eth-balance = chainlib.eth.runnable.balance:main
eth-checksum = chainlib.eth.runnable.checksum:main
eth-gas = chainlib.eth.runnable.gas:main
eth-raw = chainlib.eth.runnable.raw:main
eth-get = chainlib.eth.runnable.get:main
eth-decode = chainlib.eth.runnable.decode:main
eth-encode = chainlib.eth.runnable.encode:main
eth-info = chainlib.eth.runnable.info:main
eth-nonce = chainlib.eth.runnable.count:main
eth-wait = chainlib.eth.runnable.wait:main
eth = chainlib.eth.runnable.info:main

View File

@ -2,4 +2,3 @@ eth_tester==0.5.0b3
py-evm==0.3.0a20
rlp==2.0.1
pytest==6.0.1
coverage==5.5

View File

@ -12,7 +12,7 @@ from tests.base import TestBase
class TestChain(TestBase):
def test_chain_spec(self):
checksum_address = 'Eb3907eCad74a0013c259D5874AE7f22DcBcC95C'
checksum_address = '0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C'
plain_address = checksum_address.lower()
self.assertEqual(checksum_address, to_checksum(checksum_address))

View File

@ -7,19 +7,16 @@ import logging
import json
# external imports
from funga.eth.transaction import EIP155Transaction
from funga.eth.signer.defaultsigner import EIP155Signer
from funga.eth.keystore.dict import DictKeystore
from crypto_dev_signer.eth.transaction import EIP155Transaction
from crypto_dev_signer.eth.signer.defaultsigner import ReferenceSigner
from crypto_dev_signer.keystore.dict import DictKeystore
# local imports
import chainlib
from chainlib.eth.connection import EthUnixSignerConnection
from chainlib.eth.sign import sign_transaction
from chainlib.eth.tx import TxFactory
from chainlib.eth.address import (
to_checksum_address,
is_same_address,
)
from chainlib.eth.address import to_checksum_address
from chainlib.jsonrpc import (
jsonrpc_response,
jsonrpc_error,
@ -55,13 +52,11 @@ class Mocket(socket.socket):
logg.debug('mocket received {}'.format(v))
Mocket.req_id = o['id']
params = o['params'][0]
from_address = to_checksum_address(params.get('from'))
if not is_same_address(alice, from_address):
logg.error('from {} does not match alice {}'.format(from_address, alice)) #params))
if to_checksum_address(params.get('from')) != alice:
logg.error('from does not match alice {}'.format(params))
Mocket.error = True
to_address = to_checksum_address(params.get('to'))
if not is_same_address(bob, to_address):
logg.error('to {} does not match bob {}'.format(to_address, bob)) #params))
if to_checksum_address(params.get('to')) != bob:
logg.error('to does not match bob {}'.format(params))
Mocket.error = True
if not Mocket.error:
Mocket.tx = EIP155Transaction(params, params['nonce'], params['chainId'])
@ -97,7 +92,7 @@ class TestSign(TestBase):
logg.debug('alice {}'.format(alice))
logg.debug('bob {}'.format(bob))
self.signer = EIP155Signer(keystore)
self.signer = ReferenceSigner(keystore)
Mocket.signer = self.signer

View File

@ -23,10 +23,7 @@ from chainlib.eth.contract import (
ABIContractEncoder,
ABIContractType,
)
from chainlib.eth.address import (
to_checksum_address,
is_same_address,
)
from chainlib.eth.address import to_checksum_address
from hexathon import (
strip_0x,
add_0x,
@ -46,21 +43,8 @@ class TxTestCase(EthTesterCase):
c = Gas(signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle, chain_spec=self.chain_spec)
(tx_hash_hex, o) = c.create(self.accounts[0], self.accounts[1], 1024, tx_format=TxFormat.RLP_SIGNED)
tx = unpack(bytes.fromhex(strip_0x(o)), self.chain_spec)
self.assertTrue(is_same_address(tx['from'], self.accounts[0]))
self.assertTrue(is_same_address(tx['to'], self.accounts[1]))
def test_tx_repack(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
gas_oracle = RPCGasOracle(self.rpc)
c = Gas(signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle, chain_spec=self.chain_spec)
(tx_hash_hex, o) = c.create(self.accounts[0], self.accounts[1], 1024)
self.rpc.do(o)
o = transaction(tx_hash_hex)
tx_src = self.rpc.do(o)
tx = Tx(tx_src)
tx_bin = pack(tx.src(), self.chain_spec)
self.assertEqual(tx['from'], self.accounts[0])
self.assertEqual(tx['to'], self.accounts[1])
def test_tx_pack(self):