Compare commits

...

37 Commits

Author SHA1 Message Date
lash
a71b3d6388
Add changelog detail 2022-04-26 21:36:44 +00:00
lash
2dd92fe74b
Add fee limit, fee price to Tx object 2022-04-19 19:46:11 +00:00
lash
129e25bbf5
Bump chainlib dep 2022-04-10 19:05:58 +00:00
lash
a76020b13b
Add dict output 2022-04-10 19:04:50 +00:00
lash
3cfeb9ce84
Upgrade deps 2022-03-06 19:33:09 +00:00
lash
601db731d9
Handle int instead of hex signature values in pack from eth_tester 2022-03-01 14:43:43 +00:00
lash
c954f4d1b4
Allow encode without nonce when not tx, add rcpt src to tx 2022-03-01 07:50:30 +00:00
lash
b59eebe181
Add remaining man pages 2022-02-25 11:07:40 +00:00
lash
2f906037a9
Add more detail on examples 2022-02-24 20:05:38 +00:00
lash
1362c9aac6
WIP add eth-encode overrides 2022-02-24 19:00:54 +00:00
lash
e5e8680fc6
Improve eth-encode interface with --format and --mode options 2022-02-24 15:51:38 +00:00
lash
90d4c4eb95
Add man page for eth-encode 2022-02-24 12:07:56 +00:00
lash
edf8c4b604
Add chain spec example for eth-gas 2022-02-24 11:40:35 +00:00
lash
f61ac91205
Auto-apply overrides and examples if they exist, add examples for eth-gas 2022-02-24 11:32:33 +00:00
lash
7e6daa0be3
Add get man page, remove chain spec arg from get, info 2022-02-24 10:36:15 +00:00
lash
a3081367db
Add makefile for man 2022-02-23 13:16:04 +00:00
lash
d49e3dd6ec
Add environment description override 2022-02-23 09:47:03 +00:00
lash
d3f2b1f1fa
WIP add enviornment support 2022-02-22 14:47:06 +00:00
lash
79a1123dc8
Upgrade chainlib, allow passphrase file for wallet key 2022-02-20 18:27:51 +00:00
lash
5c09630b6e
Skip buggy hexathon 2022-02-14 13:57:37 +00:00
lash
e9f31ed7f1
Make block by number compatible with geth 2022-02-12 12:30:52 +00:00
lash
7f2c32975d
Enable unpack of pre EIP155 txs 2022-01-23 20:36:29 +00:00
lash
109666ba1d
Bump version to eliminate rlp cytoolz warnings 2022-01-21 11:22:26 +00:00
lash
8ac349f092 Revert "Bring in loglevel changes in chainlib, bump version"
This reverts commit 677b742690.
2022-01-21 00:15:33 +00:00
lash
677b742690
Bring in loglevel changes in chainlib, bump version 2022-01-21 00:14:31 +00:00
lash
96a0f3beb0
Skip compulsory exec address for offline encode, add wait to raw 2022-01-09 12:09:23 +00:00
nolash
1fbd94d382
Remove padding in hex values in encode cli command 2022-01-07 12:53:27 +00:00
nolash
1a73c0d1ed
Bump version 2022-01-07 12:27:20 +00:00
nolash
e9b7d27670
Add raw output to notx/nosign encode (args only) 2022-01-07 12:27:02 +00:00
nolash
f688cba5cc
Re-bump chainlib 2022-01-04 17:14:56 +00:00
nolash
700e668a4b
Bump chainlib 2022-01-04 17:09:49 +00:00
nolash
7832071512 Merge branch 'lash/funga' 2021-12-21 14:52:35 +00:00
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
5cfb6a7dda
Rehabilitate tests 2021-10-18 14:04:49 +02:00
38 changed files with 551 additions and 54 deletions

View File

@ -1,3 +1,26 @@
- 0.0.5-pending - 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:
* Receive all ethereum components from chainlib package * Receive all ethereum components from chainlib package
* Make settings configurable * Make settings configurable

16
Makefile Normal file
View File

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

@ -1,10 +1,11 @@
import sys
# external imports # external imports
from chainlib.jsonrpc import JSONRPCRequest from chainlib.jsonrpc import JSONRPCRequest
from chainlib.block import Block as BaseBlock from chainlib.block import Block as BaseBlock
from hexathon import ( from hexathon import (
add_0x, add_0x,
strip_0x, strip_0x,
even, compact,
) )
# local imports # local imports
@ -34,7 +35,8 @@ def block_by_hash(hsh, include_tx=True, id_generator=None):
def block_by_number(n, include_tx=True, id_generator=None): def block_by_number(n, include_tx=True, id_generator=None):
"""Implements chainlib.interface.ChainInterface method """Implements chainlib.interface.ChainInterface method
""" """
nhx = add_0x(even(hex(n)[2:])) hx = strip_0x(hex(n))
nhx = add_0x(compact(hx), compact_value=True)
j = JSONRPCRequest(id_generator) j = JSONRPCRequest(id_generator)
o = j.template() o = j.template()
o['method'] = 'eth_getBlockByNumber' o['method'] = 'eth_getBlockByNumber'

View File

@ -4,6 +4,7 @@ from chainlib.cli import (
argflag_std_read, argflag_std_read,
argflag_std_write, argflag_std_write,
argflag_std_base, argflag_std_base,
reset as argflag_reset, argflag_std_base_read,
flag_reset as argflag_reset,
Flag, Flag,
) )

View File

@ -4,6 +4,7 @@ auth =
credentials = credentials =
dialect = default dialect = default
scheme = http scheme = http
verify = 1
[chain] [chain]
spec = evm:berlin:1:ethereum spec = evm:berlin:1:ethereum

2
chainlib/eth/data/env/env.ini vendored Normal file
View File

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

View File

@ -25,7 +25,7 @@ logg = logging.getLogger()
script_dir = os.path.dirname(os.path.realpath(__file__)) script_dir = os.path.dirname(os.path.realpath(__file__))
config_dir = os.path.join(script_dir, '..', 'data', 'config') config_dir = os.path.join(script_dir, '..', 'data', 'config')
arg_flags = chainlib.eth.cli.argflag_std_read arg_flags = chainlib.eth.cli.argflag_std_base_read | chainlib.eth.cli.Flag.WALLET
argparser = chainlib.eth.cli.ArgumentParser(arg_flags) argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_positional('address', type=str, help='Ethereum address of recipient') argparser.add_positional('address', type=str, help='Ethereum address of recipient')
args = argparser.parse_args() args = argparser.parse_args()

View File

@ -11,8 +11,7 @@ import urllib
import sha3 import sha3
# external imports # external imports
import chainlib.eth.cli from chainlib.cli import flag_reset
from chainlib.eth.cli.encode import CLIEncoder
from funga.eth.signer import EIP155Signer from funga.eth.signer import EIP155Signer
from funga.eth.keystore.dict import DictKeystore from funga.eth.keystore.dict import DictKeystore
from hexathon import ( from hexathon import (
@ -21,6 +20,8 @@ from hexathon import (
) )
# local imports # local imports
import chainlib.eth.cli
from chainlib.eth.cli.encode import CLIEncoder
from chainlib.eth.constant import ZERO_ADDRESS from chainlib.eth.constant import ZERO_ADDRESS
from chainlib.eth.address import to_checksum from chainlib.eth.address import to_checksum
from chainlib.eth.connection import EthHTTPConnection from chainlib.eth.connection import EthHTTPConnection
@ -28,14 +29,6 @@ from chainlib.jsonrpc import (
JSONRPCRequest, JSONRPCRequest,
IntSequenceGenerator, IntSequenceGenerator,
) )
from chainlib.eth.nonce import (
RPCNonceOracle,
OverrideNonceOracle,
)
from chainlib.eth.gas import (
RPCGasOracle,
OverrideGasOracle,
)
from chainlib.eth.tx import ( from chainlib.eth.tx import (
TxFactory, TxFactory,
TxFormat, TxFormat,
@ -53,21 +46,20 @@ logg = logging.getLogger()
script_dir = os.path.dirname(os.path.realpath(__file__)) script_dir = os.path.dirname(os.path.realpath(__file__))
config_dir = os.path.join(script_dir, '..', 'data', 'config') config_dir = os.path.join(script_dir, '..', 'data', 'config')
arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC
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 = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_argument('--notx', type=str, help='Network send is not a transaction') 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('--signature', type=str, help='Method signature to encode')
argparser.add_argument('contract_args', type=str, nargs='*', help='arguments to encode') argparser.add_argument('contract_args', type=str, nargs='*', help='arguments to encode')
args = argparser.parse_args() args = argparser.parse_args()
extra_args = { extra_args = {
'signature': None, 'signature': None,
'contract_args': 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) 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))
if not config.get('_EXEC_ADDRESS'):
argparser.error('exec address (-e) must be defined')
block_all = args.ww block_all = args.ww
block_last = args.w or block_all block_last = args.w or block_all
@ -105,28 +97,77 @@ def main():
code += cli_encoder.get() 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 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)) print(strip_0x(code))
return return
elif not exec_address:
logg.error('exec address (-e) must be defined with mode "{}"'.format(args.mode))
sys.exit(1)
exec_address = add_0x(to_checksum_address(config.get('_EXEC_ADDRESS'))) 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 signer == None or config.true('_NOTX'): 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) c = TxFactory(chain_spec)
j = JSONRPCRequest(id_generator=rpc.id_generator) j = JSONRPCRequest(id_generator=rpc.id_generator)
o = j.template() 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['method'] = 'eth_call'
o['params'].append({ o['params'].append({
'to': exec_address, 'to': exec_address,
'from': signer_address, 'from': signer_address,
'value': '0x00', 'value': '0x0',
'gas': add_0x(int.to_bytes(8000000, 8, byteorder='big').hex()), # TODO: better get of network gas limit 'gas': gas_limit, # TODO: better get of network gas limit
'gasPrice': '0x01', 'gasPrice': gas_price,
'data': add_0x(code), 'data': add_0x(code),
}) })
height = to_blockheight_param(config.get('_HEIGHT')) height = to_blockheight_param(config.get('_HEIGHT'))
o['params'].append(height) o['params'].append(height)
o = j.finalize(o) o = j.finalize(o)
if config.get('_RPC_SEND'):
r = conn.do(o) r = conn.do(o)
try: try:
print(strip_0x(r)) print(strip_0x(r))
@ -134,6 +175,13 @@ def main():
except ValueError: except ValueError:
sys.stderr.write('query returned an empty value ({})\n'.format(r)) sys.stderr.write('query returned an empty value ({})\n'.format(r))
sys.exit(1) 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: if chain_spec == None:
raise ValueError('chain spec must be specified') raise ValueError('chain spec must be specified')

View File

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

@ -81,7 +81,7 @@ def main():
g = Gas(chain_spec, signer=signer, gas_oracle=rpc.get_gas_oracle(), nonce_oracle=rpc.get_nonce_oracle()) 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')) recipient = to_checksum_address(config.get('_RECIPIENT'))
if not config.true('_UNSAFE') and is_checksum_address(recipient): if not config.true('_UNSAFE') and not is_checksum_address(recipient):
raise ValueError('invalid checksum address') raise ValueError('invalid checksum address')
logg.info('gas transfer from {} to {} value {}'.format(signer_address, recipient, value)) logg.info('gas transfer from {} to {} value {}'.format(signer_address, recipient, value))
@ -106,7 +106,7 @@ def main():
logg.debug('sender {} balance after: {}'.format(signer_address, sender_balance)) logg.debug('sender {} balance after: {}'.format(signer_address, sender_balance))
logg.debug('recipient {} balance after: {}'.format(recipient, recipient_balance)) logg.debug('recipient {} balance after: {}'.format(recipient, recipient_balance))
if r['status'] == 0: if r['status'] == 0:
logg.critical('VM revert. Wish I could tell you more') logg.critical('VM revert for {}. Wish I could tell you more'.format(tx_hash_hex))
sys.exit(1) sys.exit(1)
print(tx_hash_hex) print(tx_hash_hex)
else: else:

View File

@ -48,7 +48,8 @@ logg = logging.getLogger()
script_dir = os.path.dirname(os.path.realpath(__file__)) script_dir = os.path.dirname(os.path.realpath(__file__))
config_dir = os.path.join(script_dir, '..', 'data', 'config') config_dir = os.path.join(script_dir, '..', 'data', 'config')
arg_flags = chainlib.eth.cli.argflag_std_read arg_flags = chainlib.eth.cli.argflag_std_base_read
arg_flags = chainlib.eth.cli.argflag_reset(arg_flags, chainlib.eth.cli.Flag.CHAIN_SPEC)
argparser = chainlib.eth.cli.ArgumentParser(arg_flags) argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_positional('item', type=str, help='Address or transaction to retrieve data for') argparser.add_positional('item', type=str, help='Address or transaction to retrieve data for')
args = argparser.parse_args() args = argparser.parse_args()
@ -98,7 +99,6 @@ def get_transaction(conn, tx_hash, id_generator):
r = conn.do(o) r = conn.do(o)
block = Block(r) block = Block(r)
tx.apply_block(block) tx.apply_block(block)
logg.debug('foo {}'.format(tx_src))
tx.generate_wire(chain_spec) tx.generate_wire(chain_spec)
return tx return tx

View File

@ -105,7 +105,10 @@ def main():
o = block_latest(id_generator=rpc.id_generator) o = block_latest(id_generator=rpc.id_generator)
r = conn.do(o) r = conn.do(o)
try:
n = int(r, 16) n = int(r, 16)
except ValueError:
n = int(r)
first_block_number = n first_block_number = n
if human: if human:
n = format(n, ',') n = format(n, ',')

View File

@ -51,6 +51,7 @@ config_dir = os.path.join(script_dir, '..', 'data', 'config')
arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC arg_flags = chainlib.eth.cli.argflag_std_write | chainlib.eth.cli.Flag.EXEC
argparser = chainlib.eth.cli.ArgumentParser(arg_flags) argparser = chainlib.eth.cli.ArgumentParser(arg_flags)
argparser.add_argument('--deploy', action='store_true', help='Deploy data as contract') argparser.add_argument('--deploy', action='store_true', help='Deploy data as contract')
argparser.add_argument('--mode', choices=['tx', 'call'], type=str, help='Mode of operation')
argparser.add_positional('data', type=str, help='Transaction data') argparser.add_positional('data', type=str, help='Transaction data')
args = argparser.parse_args() args = argparser.parse_args()
config = chainlib.eth.cli.Config.from_args(args, arg_flags, default_config_dir=config_dir) config = chainlib.eth.cli.Config.from_args(args, arg_flags, default_config_dir=config_dir)
@ -133,6 +134,11 @@ def main():
o = raw(args.data, id_generator=rpc.id_generator) o = raw(args.data, id_generator=rpc.id_generator)
if send: if send:
r = conn.do(o) 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) print(r)
else: else:
print(o) print(o)

View File

@ -95,7 +95,11 @@ def pack(tx_src, chain_spec):
tx_src['r'], tx_src['r'],
tx_src['s'], tx_src['s'],
]: ]:
for b in bytes.fromhex(strip_0x(a)): 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):
signature[cursor] = b signature[cursor] = b
cursor += 1 cursor += 1
@ -162,6 +166,7 @@ def __unpack_raw(tx_raw_bytes, chain_id=1):
vb = chain_id vb = chain_id
if chain_id != 0: if chain_id != 0:
v = int.from_bytes(d[6], 'big') v = int.from_bytes(d[6], 'big')
if v > 29:
vb = v - (chain_id * 2) - 35 vb = v - (chain_id * 2) - 35
r = bytearray(32) r = bytearray(32)
r[32-len(d[7]):] = d[7] r[32-len(d[7]):] = d[7]
@ -396,11 +401,13 @@ class TxFactory:
""" """
txe = EIP155Transaction(tx, tx['nonce'], tx['chainId']) txe = EIP155Transaction(tx, tx['nonce'], tx['chainId'])
txes = txe.serialize() txes = txe.serialize()
gas_price = strip_0x(txes['gasPrice'])
gas = strip_0x(txes['gas'])
return { return {
'from': tx['from'], 'from': tx['from'],
'to': txes['to'], 'to': txes['to'],
'gasPrice': '0x' + compact(txes['gasPrice']), 'gasPrice': add_0x(compact(gas_price)),
'gas': '0x' + compact(txes['gas']), 'gas': add_0x(compact(gas)),
'data': txes['data'], 'data': txes['data'],
} }
@ -420,6 +427,10 @@ class TxFactory:
return self.build(tx, id_generator=id_generator) return self.build(tx, id_generator=id_generator)
elif tx_format == TxFormat.RLP_SIGNED: elif tx_format == TxFormat.RLP_SIGNED:
return self.build_raw(tx) return self.build_raw(tx)
elif tx_format == TxFormat.RAW_ARGS:
return strip_0x(tx['data'])
elif tx_format == TxFormat.DICT:
return tx
raise NotImplementedError('tx formatting {} not implemented'.format(tx_format)) raise NotImplementedError('tx formatting {} not implemented'.format(tx_format))
@ -516,7 +527,7 @@ class Tx(BaseTx):
#:todo: divide up constructor method #:todo: divide up constructor method
""" """
def __init__(self, src, block=None, rcpt=None): def __init__(self, src, block=None, rcpt=None, strict=False):
self.__rcpt_block_hash = None self.__rcpt_block_hash = None
src = self.src_normalize(src) src = self.src_normalize(src)
@ -545,6 +556,9 @@ class Tx(BaseTx):
self.outputs = [to_checksum(address_from)] self.outputs = [to_checksum(address_from)]
self.contract = None self.contract = None
self.fee_limit = self.gas_limit
self.fee_price = self.gas_price
try: try:
inpt = src['input'] inpt = src['input']
except KeyError: except KeyError:
@ -566,13 +580,14 @@ class Tx(BaseTx):
try: try:
self.wire = src['raw'] self.wire = src['raw']
except KeyError: except KeyError:
logg.warning('no inline raw tx src, and no raw rendering implemented, field will be "None"') logg.debug('no inline raw tx src, and no raw rendering implemented, field will be "None"')
self.status = Status.PENDING self.status = Status.PENDING
self.logs = None self.logs = None
self.tx_rcpt_src = None
if rcpt != None: if rcpt != None:
self.apply_receipt(rcpt) self.apply_receipt(rcpt, strict=strict)
self.v = src.get('v') self.v = src.get('v')
self.r = src.get('r') self.r = src.get('r')
@ -615,7 +630,11 @@ class Tx(BaseTx):
return self.src() return self.src()
def apply_receipt(self, rcpt): def rcpt_src(self):
return self.tx_rcpt_src
def apply_receipt(self, rcpt, strict=False):
"""Apply receipt data to transaction object. """Apply receipt data to transaction object.
Effect is the same as passing a receipt at construction. Effect is the same as passing a receipt at construction.
@ -625,6 +644,7 @@ class Tx(BaseTx):
""" """
rcpt = self.src_normalize(rcpt) rcpt = self.src_normalize(rcpt)
logg.debug('rcpt {}'.format(rcpt)) logg.debug('rcpt {}'.format(rcpt))
self.tx_rcpt_src = rcpt
tx_hash = add_0x(rcpt['transaction_hash']) tx_hash = add_0x(rcpt['transaction_hash'])
if rcpt['transaction_hash'] != add_0x(self.hash): if rcpt['transaction_hash'] != add_0x(self.hash):
@ -639,6 +659,12 @@ class Tx(BaseTx):
status_number = int(rcpt['status'], 16) status_number = int(rcpt['status'], 16)
except TypeError: except TypeError:
status_number = int(rcpt['status']) 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: if rcpt['block_number'] == None:
self.status = Status.PENDING self.status = Status.PENDING
else: else:
@ -693,12 +719,12 @@ class Tx(BaseTx):
@staticmethod @staticmethod
def from_src(src, block=None, rcpt=None): def from_src(src, block=None, rcpt=None, strict=False):
"""Creates a new Tx object. """Creates a new Tx object.
Alias of constructor. Alias of constructor.
""" """
return Tx(src, block=block, rcpt=rcpt) return Tx(src, block=block, rcpt=rcpt, strict=strict)
def __str__(self): def __str__(self):

View File

@ -0,0 +1,16 @@
.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

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

View File

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

@ -0,0 +1,11 @@
.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.

15
man/eth-count.head.groff Normal file
View File

@ -0,0 +1,15 @@
.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

1
man/eth-count.overrides Normal file
View File

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

14
man/eth-decode.head.groff Normal file
View File

@ -0,0 +1,14 @@
.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

@ -0,0 +1,31 @@
.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

@ -0,0 +1,22 @@
.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

17
man/eth-encode.head.groff Normal file
View File

@ -0,0 +1,17 @@
.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

1
man/eth-encode.overrides Normal file
View File

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

View File

@ -0,0 +1,75 @@
.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)

19
man/eth-gas.head.groff Normal file
View File

@ -0,0 +1,19 @@
.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

1
man/eth-gas.overrides Normal file
View File

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

19
man/eth-get.head.groff Normal file
View File

@ -0,0 +1,19 @@
.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

27
man/eth-info.head.groff Normal file
View File

@ -0,0 +1,27 @@
.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

@ -0,0 +1,8 @@
.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

26
man/eth-raw.head.groff Normal file
View File

@ -0,0 +1,26 @@
.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

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

17
man/eth-wait.head.groff Normal file
View File

@ -0,0 +1,17 @@
.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

2
man/eth-wait.overrides Normal file
View File

@ -0,0 +1,2 @@
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.5.1 funga-eth~=0.6.0
pysha3==1.0.2 pysha3==1.0.2
hexathon~=0.1.0 hexathon~=0.1.5
websocket-client==0.57.0 websocket-client==0.57.0
potaahto~=0.1.0 potaahto~=0.1.1
chainlib~=0.0.12 chainlib~=0.1.0
confini~=0.5.1 confini~=0.6.0

View File

@ -1,6 +1,6 @@
[metadata] [metadata]
name = chainlib-eth name = chainlib-eth
version = 0.0.13a1 version = 0.1.1
description = Ethereum implementation of the chainlib interface description = Ethereum implementation of the chainlib interface
author = Louis Holbrook author = Louis Holbrook
author_email = dev@holbrook.no author_email = dev@holbrook.no
@ -48,4 +48,3 @@ console_scripts =
eth-info = chainlib.eth.runnable.info:main eth-info = chainlib.eth.runnable.info:main
eth-nonce = chainlib.eth.runnable.count:main eth-nonce = chainlib.eth.runnable.count:main
eth-wait = chainlib.eth.runnable.wait:main eth-wait = chainlib.eth.runnable.wait:main
eth = chainlib.eth.runnable.info:main

View File

@ -50,6 +50,19 @@ class TxTestCase(EthTesterCase):
self.assertTrue(is_same_address(tx['to'], self.accounts[1])) 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)
def test_tx_pack(self): def test_tx_pack(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
gas_oracle = RPCGasOracle(self.rpc) gas_oracle = RPCGasOracle(self.rpc)