32 Commits

Author SHA1 Message Date
nolash
f618a5c9d7 Add missing rpc verify config directive 2021-12-21 14:50:38 +00:00
nolash
6bc0caaad7 Correct inverted addess checksum check for gas cli 2021-12-10 02:35:38 +01:00
nolash
2ef596df86 Upgrade chainlib and self 2021-12-06 19:03:08 +01:00
nolash
bfbb1dea26 Add raw args option 2021-12-06 19:00:55 +01:00
nolash
6474a2399a Enable call when wallet set for encode cli command 2021-11-15 14:29:40 +01:00
nolash
d3c73c7e5b Upgrade deps. commit patch version 2021-11-10 09:59:36 +01:00
nolash
80e5339886 Fix wrong symbol for rpc call in eth-encode 2021-11-04 12:20:42 +01:00
nolash
53cea0817e Fix remaining 0x problems 2021-10-30 16:49:47 +02:00
nolash
5f9634d5a4 Upgrade chainlib 2021-10-28 12:17:32 +02:00
nolash
6ee3ea4638 Handle null-value block hashes on receipts 2021-10-25 11:25:03 +02:00
nolash
31e75f60de Fix missing conflict resolution in eth-encode, non-0x address fallthrough in eth-info 2021-10-25 09:57:31 +02:00
nolash
b51e5dc408 Bump chainlib 2021-10-21 07:28:00 +02:00
nolash
8c23535ffe Add wait for hashes script 2021-10-19 19:40:53 +02:00
nolash
6cadad372f Upgrade deps 2021-10-18 14:27:40 +02:00
nolash
47b7fa7eef Replace crypto_dev_signer with funga 2021-10-18 14:23:54 +02:00
nolash
2204c512c3 Rehabilitate tests 2021-10-18 13:57:42 +02:00
nolash
c66f5fdff9 Merge branch 'master' into 0.0.10-dev 2021-10-18 13:32:11 +02:00
nolash
700c52089e Merge branch 'master' of file:///home/lash/src/client/cic/grassrootseconomics/chainlib-eth 2021-10-18 13:31:18 +02:00
nolash
76c8ff58fc Merge branch 'lash/cli-encode' 2021-10-18 13:30:06 +02:00
nolash
7abce82eef Fix wrong variable name in encode cli 2021-10-18 13:28:53 +02:00
nolash
d6b258f214 Merge remote-tracking branch 'origin/master' into lash/schmerge 2021-10-18 12:52:26 +02:00
Louis Holbrook
91d748d09f Merge branch 'lash/gas-checksum-cli' into 'master'
bug: Correct rpc format for gas cli request

See merge request chaintool/chainlib-eth!10
2021-10-18 10:49:40 +00:00
Louis Holbrook
a188c4e6bb bug: Correct rpc format for gas cli request 2021-10-18 10:49:40 +00:00
Louis Holbrook
778994e07e Merge branch 'lash/cli-encode' into 'master'
feat: Add generic contract tx/call encoder cli tool

See merge request chaintool/chainlib-eth!6
2021-10-18 10:18:21 +00:00
Louis Holbrook
94e016366b feat: Add generic contract tx/call encoder cli tool 2021-10-18 10:18:20 +00:00
Louis Holbrook
2011ce8bb8 Merge branch 'lash/normalize-verify-sig' into 'master'
refactor: Normalize signature verification

See merge request chaintool/chainlib-eth!9
2021-10-18 10:07:03 +00:00
Louis Holbrook
ab8c510fa0 refactor: Normalize signature verification 2021-10-18 10:07:03 +00:00
nolash
b983938d80 Move all encoding steps to cliencoder 2021-10-06 07:52:19 +02:00
nolash
0e0dbf180e Split cli helper classes to respective files 2021-10-06 07:44:53 +02:00
nolash
e4d6b8d845 Add option to generate abi encoding without method 2021-10-06 07:30:07 +02:00
nolash
75c72b7828 Incorporate reset method for arg flags 2021-10-06 07:13:12 +02:00
nolash
55a72ed6d6 Add generic contract call/tx encoder tool 2021-10-06 06:57:51 +02:00
32 changed files with 642 additions and 164 deletions

View File

@@ -1,3 +1,5 @@
- 0.0.5-pending
- 0.0.15:
* Correct inverted addess checksum check for gas cli
- 0.0.5-unreleased:
* Receive all ethereum components from chainlib package
* Make settings configurable

View File

@@ -4,7 +4,7 @@ from hexathon import (
strip_0x,
uniform,
)
from crypto_dev_signer.encoding import (
from funga.eth.encoding import (
is_address,
is_checksum_address,
to_checksum_address,
@@ -42,3 +42,7 @@ 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

@@ -60,6 +60,20 @@ 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

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

9
chainlib/eth/cli/arg.py Normal file
View File

@@ -0,0 +1,9 @@
# external imports
from chainlib.cli import (
ArgumentParser,
argflag_std_read,
argflag_std_write,
argflag_std_base,
reset as argflag_reset,
Flag,
)

View File

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

103
chainlib/eth/cli/encode.py Normal file
View File

@@ -0,0 +1,103 @@
# 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,22 +1,8 @@
# standard imports
import os
import logging
# external imports
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
from chainlib.cli import Rpc as BaseRpc
from chainlib.eth.connection import EthHTTPConnection
# local imports
from chainlib.eth.address import AddressChecksum
from chainlib.eth.connection import EthHTTPConnection
from chainlib.eth.gas import (
OverrideGasOracle,
RPCGasOracle,
@@ -26,20 +12,6 @@ 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):
@@ -97,27 +69,3 @@ 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

@@ -0,0 +1,20 @@
# 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,6 +36,7 @@ from chainlib.jsonrpc import (
from chainlib.eth.tx import (
unpack,
)
from potaahto.symbols import snake_and_camel
logg = logging.getLogger(__name__)
@@ -88,11 +89,16 @@ class EthHTTPConnection(JSONRPCHTTPConnection):
e = jsonrpc_result(r, error_parser)
if e != None:
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)
return e
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('e {}'.format(strip_0x(e['status'])))
if strip_0x(e['status']) == '00':
raise RevertEthException(tx_hash_hex)
return e
if timeout > 0.0:
delta = (datetime.datetime.utcnow() - t) + datetime.timedelta(seconds=delay)

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:ethereum:1
spec = evm:berlin:1:ethereum
[wallet]
key_file =

View File

@@ -6,7 +6,7 @@ from hexathon import (
add_0x,
strip_0x,
)
from crypto_dev_signer.eth.transaction import EIP155Transaction
from funga.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(address)
o['params'].append(add_0x(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__(address)
super(NonceOracle, self).__init__(add_0x(address))
def get_nonce(self):

View File

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

View File

@@ -3,7 +3,6 @@
# 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 crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from funga.eth.signer import 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 crypto_dev_signer.keystore.dict import DictKeystore
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from funga.eth.keystore.dict import DictKeystore
from funga.eth.signer import EIP155Signer
from hexathon import add_0x
logging.basicConfig(level=logging.WARNING)
@@ -43,7 +43,7 @@ conn = rpc.connect_by_config(config)
def main():
# TODO: should tolerate if address not prefixed with 0x
o = count(holder_address, id_generator=rpc.id_generator)
o = count(add_0x(holder_address), id_generator=rpc.id_generator)
r = conn.do(o)
count_result = None
try:

View File

@@ -0,0 +1,157 @@
# 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
import chainlib.eth.cli
from chainlib.eth.cli.encode import CLIEncoder
from funga.eth.signer import EIP155Signer
from funga.eth.keystore.dict import DictKeystore
from hexathon import (
add_0x,
strip_0x,
)
# local imports
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.nonce import (
RPCNonceOracle,
OverrideNonceOracle,
)
from chainlib.eth.gas import (
RPCGasOracle,
OverrideGasOracle,
)
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
argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_argument('--notx', action='store_true', help='Network send is not a transaction')
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,
'notx': None,
}
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args=extra_args, default_config_dir=config_dir)
if not config.get('_EXEC_ADDRESS'):
argparser.error('exec address (-e) must be defined')
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()
if not config.get('_SIGNATURE'):
print(strip_0x(code))
return
exec_address = add_0x(to_checksum_address(config.get('_EXEC_ADDRESS')))
if signer == None or config.true('_NOTX'):
c = TxFactory(chain_spec)
j = JSONRPCRequest(id_generator=rpc.id_generator)
o = j.template()
o['method'] = 'eth_call'
o['params'].append({
'to': exec_address,
'from': signer_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(code),
})
height = to_blockheight_param(config.get('_HEIGHT'))
o['params'].append(height)
o = j.finalize(o)
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)
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

@@ -26,6 +26,10 @@ 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)
@@ -62,8 +66,12 @@ send = config.true('_RPC_SEND')
def balance(address, id_generator):
o = gas_balance(address, id_generator=id_generator)
r = conn.do(o)
hx = strip_0x(r)
return int(hx, 16)
try:
balance = int(r)
except ValueError:
balance = strip_0x(r)
balance = int(balance, 16)
return balance
def main():
@@ -73,28 +81,28 @@ 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 recipient != add_0x(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(signer_address, rpc.id_generator)
recipient_balance = balance(recipient, rpc.id_generator)
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))
except urllib.error.URLError:
pass
(tx_hash_hex, o) = g.create(signer_address, recipient, value, data=config.get('_DATA'), id_generator=rpc.id_generator)
(tx_hash_hex, o) = g.create(signer_address, add_0x(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(signer_address, rpc.id_generator)
recipient_balance = balance(recipient, rpc.id_generator)
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))
if r['status'] == 0:

View File

@@ -63,6 +63,7 @@ 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'
@@ -97,13 +98,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 crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from funga.eth.signer import EIP155Signer
# local imports
from chainlib.eth.address import AddressChecksum
@@ -24,6 +24,7 @@ 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
@@ -43,21 +44,35 @@ 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()
config = chainlib.eth.cli.Config.from_args(args, arg_flags, extra_args={'long': None}, default_config_dir=config_dir)
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)
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)
if config.get('_ENTRY') != None:
if config.get('_ENTRY') not in results_translation.keys():
raise ValueError('Unknown entry {}'.format(config.get('_ENTRY')))
rpc = chainlib.eth.cli.Rpc(wallet=wallet)
rpc = chainlib.eth.cli.Rpc()
conn = rpc.connect_by_config(config)
token_symbol = 'eth'
@@ -68,12 +83,25 @@ 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, ',')
sys.stdout.write('Network id: {}\n'.format(r))
if set_result(results, 'network_id', r):
return
o = block_latest(id_generator=rpc.id_generator)
r = conn.do(o)
@@ -81,7 +109,8 @@ def main():
first_block_number = n
if human:
n = format(n, ',')
sys.stdout.write('Block: {}\n'.format(n))
if set_result(results, 'block', n):
return
o = block_by_number(first_block_number, False, id_generator=rpc.id_generator)
r = conn.do(o)
@@ -107,22 +136,31 @@ def main():
if human:
n = format(n, ',')
sys.stdout.write('Gaslimit: {}\n'.format(n))
sys.stdout.write('Blocktime: {}\n'.format(aggr_time / BLOCK_SAMPLES))
if set_result(results, 'gas_limit', n):
return
if set_result(results, 'block_time', aggr_time / BLOCK_SAMPLES):
return
o = price(id_generator=rpc.id_generator)
r = conn.do(o)
n = int(r, 16)
if human:
n = format(n, ',')
sys.stdout.write('Gasprice: {}\n'.format(n))
if set_result(results, 'gas_price', n):
return
if holder_address != None:
o = count(holder_address)
if config.get('_LOCAL'):
o = syncing()
r = conn.do(o)
n = int(r, 16)
sys.stdout.write('Address: {}\n'.format(holder_address))
sys.stdout.write('Nonce: {}\n'.format(n))
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]))
if __name__ == '__main__':

View File

@@ -11,8 +11,8 @@ import urllib
# external imports
import chainlib.eth.cli
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from crypto_dev_signer.keystore.dict import DictKeystore
from funga.eth.signer import EIP155Signer
from funga.eth.keystore.dict import DictKeystore
from hexathon import (
add_0x,
strip_0x,
@@ -50,6 +50,7 @@ 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_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)
@@ -65,9 +66,6 @@ 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'))
@@ -83,34 +81,37 @@ def main():
except SignerMissingException:
pass
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):
raise ValueError('invalid checksum address')
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')
j = JSONRPCRequest(id_generator=rpc.id_generator)
o = j.template()
o['method'] = 'eth_call'
o['params'].append({
'to': exec_address,
'from': signer_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),
})
height = to_blockheight_param(config.get('_HEIGHT'))
o['params'].append(height)
o = j.finalize(o)
r = conn.do(o)
try:
print(strip_0x(r))
except ValueError:
sys.stderr.write('query returned an empty value\n')
sys.exit(1)
return
if signer_address == None:
j = JSONRPCRequest(id_generator=rpc.id_generator)
o = j.template()
o['method'] = 'eth_call'
o['params'].append({
'to': exec_address,
'from': signer_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),
})
height = to_blockheight_param(config.get('_HEIGHT'))
o['params'].append(height)
o = j.finalize(o)
r = conn.do(o)
try:
print(strip_0x(r))
except ValueError:
sys.stderr.write('query returned an empty value ({})\n'.format(r))
sys.exit(1)
if signer_address != None:
else:
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())
@@ -124,8 +125,9 @@ 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)

View File

@@ -0,0 +1,114 @@
# 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,9 +13,11 @@ from hexathon import (
)
from rlp import decode as rlp_decode
from rlp import encode as rlp_encode
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 funga.eth.transaction import EIP155Transaction
from funga.eth.encoding import (
public_key_to_address,
chain_id_to_v,
)
from potaahto.symbols import snake_and_camel
from chainlib.hash import keccak256_hex_to_hex
from chainlib.status import Status
@@ -167,7 +169,6 @@ 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))
@@ -299,7 +300,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 of chainlib.eth.connection.sign_transaction_to_wire
:type param: Object implementing interface ofchainlib.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
@@ -420,7 +421,7 @@ class TxFactory:
elif tx_format == TxFormat.RLP_SIGNED:
return self.build_raw(tx)
elif tx_format == TxFormat.RAW_ARGS:
return tx['data']
return strip_0x(tx['data'])
raise NotImplementedError('tx formatting {} not implemented'.format(tx_format))

View File

@@ -25,8 +25,8 @@ from hexathon import (
)
from chainlib.eth.tx import receipt
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from crypto_dev_signer.encoding import private_key_to_address
from funga.eth.signer import EIP155Signer
from funga.eth.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 crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
from crypto_dev_signer.keystore.dict import DictKeystore
from funga.eth.signer import EIP155Signer
from funga.eth.keystore.dict import DictKeystore
from hexathon import (
strip_0x,
add_0x,
@@ -26,7 +26,8 @@ from chainlib.connection import (
from chainlib.eth.address import to_checksum_address
from chainlib.chain import ChainSpec
logg = logging.getLogger(__name__)
#logg = logging.getLogger(__name__)
logg = logging.getLogger()
test_address = bytes.fromhex('Eb3907eCad74a0013c259D5874AE7f22DcBcC95C')
@@ -63,6 +64,7 @@ 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)))
@@ -73,7 +75,6 @@ class EthTesterCase(unittest.TestCase):
RPCConnection.register_constructor(ConnType.CUSTOM, rpc_with_tester, tag='signer')
RPCConnection.register_location('custom', self.chain_spec, tag='default', exist_ok=True)
RPCConnection.register_location('custom', self.chain_spec, tag='signer', exist_ok=True)
def tearDown(self):

View File

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

View File

@@ -1,6 +1,6 @@
[metadata]
name = chainlib-eth
version = 0.0.10a2
version = 0.0.15
description = Ethereum implementation of the chainlib interface
author = Louis Holbrook
author_email = dev@holbrook.no
@@ -14,6 +14,7 @@ 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+)
@@ -32,15 +33,19 @@ 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,3 +2,4 @@ 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 = '0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C'
checksum_address = 'Eb3907eCad74a0013c259D5874AE7f22DcBcC95C'
plain_address = checksum_address.lower()
self.assertEqual(checksum_address, to_checksum(checksum_address))

View File

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

View File

@@ -23,7 +23,10 @@ from chainlib.eth.contract import (
ABIContractEncoder,
ABIContractType,
)
from chainlib.eth.address import to_checksum_address
from chainlib.eth.address import (
to_checksum_address,
is_same_address,
)
from hexathon import (
strip_0x,
add_0x,
@@ -43,8 +46,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.assertEqual(tx['from'], self.accounts[0])
self.assertEqual(tx['to'], self.accounts[1])
self.assertTrue(is_same_address(tx['from'], self.accounts[0]))
self.assertTrue(is_same_address(tx['to'], self.accounts[1]))
def test_tx_pack(self):