commit 95474ddff55ed3049282a01657b1740f5f42d5cd
parent 607b1a33712d7e7b75f6256a74c728961ef59f11
Author: Louis Holbrook <accounts-gitlab@holbrook.no>
Date: Wed, 31 Mar 2021 14:07:23 +0000
Merge branch 'lash/chainlib' into 'master'
Lash/chainlib
See merge request nolash/eth-accounts-index!1
Diffstat:
10 files changed, 481 insertions(+), 291 deletions(-)
diff --git a/python/CHANGELOG b/python/CHANGELOG
@@ -1,4 +1,6 @@
-- 0.0.10-unreleased
+- 0.0.11
+ - Add list cli command
+- 0.0.10
- Implement external signer
- Standardize cli arg flags
- Rename entry point executable names in setup
diff --git a/python/eth_accounts_index/registry.py b/python/eth_accounts_index/registry.py
@@ -1,31 +1,40 @@
+# standard imports
import logging
import json
import os
+# external imports
+from chainlib.eth.tx import (
+ TxFactory,
+ TxFormat,
+ )
+from chainlib.eth.contract import (
+ ABIContractEncoder,
+ ABIContractDecoder,
+ ABIContractType,
+ abi_decode_single,
+ )
+from chainlib.eth.constant import ZERO_ADDRESS
+from chainlib.jsonrpc import (
+ jsonrpc_template,
+ )
+from chainlib.eth.error import RequestMismatchException
+from hexathon import (
+ add_0x,
+ strip_0x,
+ )
+
logg = logging.getLogger()
moddir = os.path.dirname(__file__)
datadir = os.path.join(moddir, 'data')
-class AccountRegistry:
+class AccountRegistry(TxFactory):
__abi = None
__bytecode = None
- def __init__(self, w3, address, signer_address=None):
- abi = AccountRegistry.abi()
- AccountRegistry.bytecode()
- self.contract = w3.eth.contract(abi=abi, address=address)
- self.w3 = w3
- if signer_address != None:
- self.signer_address = signer_address
- else:
- if type(self.w3.eth.defaultAccount).__name__ == 'Empty':
- self.w3.eth.defaultAccount = self.w3.eth.accounts[0]
- self.signer_address = self.w3.eth.defaultAccount
-
-
@staticmethod
def abi():
if AccountRegistry.__abi == None:
@@ -44,45 +53,109 @@ class AccountRegistry:
return AccountRegistry.__bytecode
- def add(self, address):
- gasPrice = self.w3.eth.gasPrice;
- nonce = self.w3.eth.getTransactionCount(self.signer_address, 'pending')
- tx = self.contract.functions.add(address).buildTransaction({
- 'gasPrice': gasPrice,
- 'gas': 100000,
- 'from': self.signer_address,
- 'nonce': nonce,
- })
- logg.debug('tx {}'.format(tx))
- tx_hash = self.contract.functions.add(address).transact({
- 'gasPrice': gasPrice,
- 'gas': 100000,
- 'from': self.signer_address,
- 'nonce': nonce,
- })
- return tx_hash
-
-
- def count(self):
- return self.contract.functions.count().call()
-
-
- def have(self, address):
- r = self.contract.functions.accountsIndex(address).call()
- return r != 0
-
-
- def get_index(self, i):
- return self.contract.functions.accounts(i).call()
-
-
- def last(self, n):
- c = self.count()
- lo = c - n - 1
- if lo < 0:
- lo = 0
- accounts = []
- for i in range(c - 1, lo, -1):
- a = self.contract.functions.accounts(i).call()
- accounts.append(a)
- return accounts
+ @staticmethod
+ def gas(code=None):
+ return 700000
+
+
+ def constructor(self, sender_address):
+ code = AccountRegistry.bytecode()
+ tx = self.template(sender_address, None, use_nonce=True)
+ tx = self.set_code(tx, code)
+ return self.build(tx)
+
+
+ def add(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
+ return self.__single_address_method('add', contract_address, sender_address, address, tx_format)
+
+
+ def add_writer(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
+ return self.__single_address_method('addWriter', contract_address, sender_address, address, tx_format)
+
+
+ def delete_writer(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
+ return self.__single_address_method('deleteWriter', contract_address, sender_address, address, tx_format)
+
+
+ def __single_address_method(self, method, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
+ enc = ABIContractEncoder()
+ enc.method(method)
+ enc.typ(ABIContractType.ADDRESS)
+ enc.address(address)
+ data = enc.get()
+ tx = self.template(sender_address, contract_address, use_nonce=True)
+ tx = self.set_code(tx, data)
+ tx = self.finalize(tx, tx_format)
+ return tx
+
+
+ def have(self, contract_address, address, sender_address=ZERO_ADDRESS):
+ o = jsonrpc_template()
+ o['method'] = 'eth_call'
+ enc = ABIContractEncoder()
+ enc.method('have')
+ enc.typ(ABIContractType.ADDRESS)
+ enc.address(address)
+ data = add_0x(enc.get())
+ tx = self.template(sender_address, contract_address)
+ tx = self.set_code(tx, data)
+ o['params'].append(self.normalize(tx))
+ return o
+
+
+ def count(self, contract_address, sender_address=ZERO_ADDRESS):
+ o = jsonrpc_template()
+ o['method'] = 'eth_call'
+ enc = ABIContractEncoder()
+ enc.method('count')
+ data = add_0x(enc.get())
+ tx = self.template(sender_address, contract_address)
+ tx = self.set_code(tx, data)
+ o['params'].append(self.normalize(tx))
+ return o
+
+
+ def account(self, contract_address, idx, sender_address=ZERO_ADDRESS):
+ o = jsonrpc_template()
+ o['method'] = 'eth_call'
+ enc = ABIContractEncoder()
+ enc.method('accounts')
+ enc.typ(ABIContractType.UINT256)
+ enc.uint256(idx)
+ data = add_0x(enc.get())
+ tx = self.template(sender_address, contract_address)
+ tx = self.set_code(tx, data)
+ o['params'].append(self.normalize(tx))
+ return o
+
+
+ @classmethod
+ def parse_account(self, v):
+ return abi_decode_single(ABIContractType.ADDRESS, v)
+
+
+ @classmethod
+ def parse_have(self, v):
+ return abi_decode_single(ABIContractType.BOOLEAN, v)
+
+
+ @classmethod
+ def parse_add_request(self, v):
+ v = strip_0x(v)
+ cursor = 0
+ enc = ABIContractEncoder()
+ enc.method('add')
+ enc.typ(ABIContractType.ADDRESS)
+ r = enc.get()
+ l = len(r)
+ m = v[:l]
+ if m != r:
+ logg.error('method mismatch, expected {}, got {}'.format(r, m))
+ raise RequestMismatchException(v)
+ cursor += l
+
+ dec = ABIContractDecoder()
+ dec.typ(ABIContractType.ADDRESS)
+ dec.val(v[cursor:cursor+64])
+ r = dec.decode()
+ return r
diff --git a/python/eth_accounts_index/runnable/add.py b/python/eth_accounts_index/runnable/add.py
@@ -1,4 +1,4 @@
-"""Adds writers and/or account entries to accounts index
+"""Adds account entries to accounts index
.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
@@ -10,20 +10,23 @@ import os
import json
import argparse
import logging
+import sys
# third-party imports
-import web3
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
-from crypto_dev_signer.keystore import DictKeystore
-from crypto_dev_signer.eth.helper import EthTxExecutor
+from crypto_dev_signer.keystore.dict import DictKeystore
from chainlib.chain import ChainSpec
+from chainlib.eth.nonce import RPCNonceOracle
+from chainlib.eth.gas import RPCGasOracle
+from chainlib.eth.connection import EthHTTPConnection
+from chainlib.eth.tx import receipt
+
+# local imports
+from eth_accounts_index import AccountRegistry
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
-logging.getLogger('web3').setLevel(logging.WARNING)
-logging.getLogger('urllib3').setLevel(logging.WARNING)
-
script_dir = os.path.dirname(__file__)
data_dir = os.path.join(script_dir, '..', 'data')
@@ -31,15 +34,13 @@ argparser = argparse.ArgumentParser()
argparser.add_argument('-p', '--provider', dest='p', default='http://localhost:8545', type=str, help='Web3 provider url (http only)')
argparser.add_argument('-w', action='store_true', help='Wait for the last transaction to be confirmed')
argparser.add_argument('-ww', action='store_true', help='Wait for every transaction to be confirmed')
-argparser.add_argument('--writer', dest='writer', action='append', type=str, help='Writer to add')
-argparser.add_argument('-a', '--signer-address', dest='o', type=str, help='Accounts index owner')
argparser.add_argument('-i', '--chain-spec', dest='i', type=str, default='Ethereum:1', help='Chain specification string')
-argparser.add_argument('--account', dest='account', action='append', type=str, help='Account to add')
argparser.add_argument('-y', '--key-file', dest='y', type=str, help='Ethereum keystore file to use for signing')
-argparser.add_argument('-r', '--accounts-index-address', dest='r', required=True, type=str, help='Contract address to account index to edit')
-argparser.add_argument('--abi-dir', dest='abi_dir', type=str, default=data_dir, help='Directory containing bytecode and abi (default: {})'.format(data_dir))
+argparser.add_argument('-a', '--contract-address', dest='a', required=True, type=str, help='Contract address to account index to edit')
argparser.add_argument('-v', action='store_true', help='Be verbose')
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
+argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration')
+argparser.add_argument('address', type=str, help='Address to add')
args = argparser.parse_args()
if args.vv:
@@ -50,55 +51,45 @@ elif args.v:
block_last = args.w
block_all = args.ww
-w3 = web3.Web3(web3.Web3.HTTPProvider(args.p))
+passphrase_env = 'ETH_PASSPHRASE'
+if args.env_prefix != None:
+ passphrase_env = args.env_prefix + '_' + passphrase_env
+passphrase = os.environ.get(passphrase_env)
+if passphrase == None:
+ logg.warning('no passphrase given')
+ passphrase=''
signer_address = None
keystore = DictKeystore()
if args.y != None:
logg.debug('loading keystore file {}'.format(args.y))
- signer_address = keystore.import_keystore_file(args.y)
+ signer_address = keystore.import_keystore_file(args.y, password=passphrase)
logg.debug('now have key for signer address {}'.format(signer_address))
signer = EIP155Signer(keystore)
chain_spec = ChainSpec.from_chain_str(args.i)
chain_id = chain_spec.network_id()
-helper = EthTxExecutor(
- w3,
- signer_address,
- signer,
- chain_id,
- block=args.ww,
- )
+rpc = EthHTTPConnection(args.p)
+nonce_oracle = RPCNonceOracle(signer_address, rpc)
+gas_oracle = RPCGasOracle(rpc, code_callback=AccountRegistry.gas)
+contract_address = args.a
+account = args.address
def main():
- f = open(os.path.join(args.abi_dir, 'AccountsIndex.json'), 'r')
- abi = json.load(f)
- f.close()
-
- c = w3.eth.contract(abi=abi, address=args.r)
-
- if args.writer != None:
- for w in args.writer:
- logg.info('adding {} to write list'.format(w))
- (tx_hash, rcpt) = helper.sign_and_send(
- [
- c.functions.addWriter(w).buildTransaction,
- ],
- )
- print(tx_hash)
-
-
- if args.account != None:
- for a in args.account:
- logg.info('adding {} to accounts index'.format(a))
- (tx_hash, rcpt) = helper.sign_and_send(
- [
- c.functions.add(a).buildTransaction,
- ],
- )
- print(tx_hash)
+
+ if __name__ == '__main__':
+ c = AccountRegistry(signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=chain_id)
+ (tx_hash_hex, o) = c.add(contract_address, signer_address, account)
+ rpc.do(o)
+ r = rpc.wait(tx_hash_hex)
+ if block_last:
+ if r['status'] == 0:
+ sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you')
+ sys.exit(1)
+
+ print(tx_hash_hex)
if __name__ == '__main__':
diff --git a/python/eth_accounts_index/runnable/deploy.py b/python/eth_accounts_index/runnable/deploy.py
@@ -6,24 +6,27 @@
"""
# standard imports
+import sys
import os
import json
import argparse
import logging
# third-party imports
-import web3
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
-from crypto_dev_signer.keystore import DictKeystore
-from crypto_dev_signer.eth.helper import EthTxExecutor
+from crypto_dev_signer.keystore.dict import DictKeystore
from chainlib.chain import ChainSpec
+from chainlib.eth.nonce import RPCNonceOracle
+from chainlib.eth.gas import RPCGasOracle
+from chainlib.eth.connection import EthHTTPConnection
+from chainlib.eth.tx import receipt
+
+# local imports
+from eth_accounts_index import AccountRegistry
logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
-logging.getLogger('web3').setLevel(logging.WARNING)
-logging.getLogger('urllib3').setLevel(logging.WARNING)
-
script_dir = os.path.dirname(__file__)
data_dir = os.path.join(script_dir, '..', 'data')
@@ -32,14 +35,10 @@ argparser.add_argument('-p', '--provider', dest='p', default='http://localhost:8
argparser.add_argument('-w', action='store_true', help='Wait for the last transaction to be confirmed')
argparser.add_argument('-ww', action='store_true', help='Wait for every transaction to be confirmed')
argparser.add_argument('-i', '--chain-spec', dest='i', type=str, default='Ethereum:1', help='Chain specification string')
-argparser.add_argument('--writer', dest='writer', action='append', type=str, help='Writer to add')
-argparser.add_argument('-a', '--signer-address', dest='a', type=str, help='Signing address')
argparser.add_argument('-y', '--key-file', dest='y', type=str, help='Ethereum keystore file to use for signing')
-argparser.add_argument('--account', action='append', type=str, help='Account to add')
-argparser.add_argument('--keep-sender', dest='keep_sender', action='store_true', help='If set, sender will be kept as writer')
-argparser.add_argument('--abi-dir', dest='abi_dir', type=str, default=data_dir, help='Directory containing bytecode and abi (default: {})'.format(data_dir))
argparser.add_argument('-v', action='store_true', help='Be verbose')
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
+argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration')
args = argparser.parse_args()
if args.vv:
@@ -50,86 +49,47 @@ elif args.v:
block_last = args.w
block_all = args.ww
-w3 = web3.Web3(web3.Web3.HTTPProvider(args.p))
+passphrase_env = 'ETH_PASSPHRASE'
+if args.env_prefix != None:
+ passphrase_env = args.env_prefix + '_' + passphrase_env
+passphrase = os.environ.get(passphrase_env)
+if passphrase == None:
+ logg.warning('no passphrase given')
+ passphrase=''
signer_address = None
keystore = DictKeystore()
if args.y != None:
logg.debug('loading keystore file {}'.format(args.y))
- signer_address = keystore.import_keystore_file(args.y)
+ signer_address = keystore.import_keystore_file(args.y, password=passphrase)
logg.debug('now have key for signer address {}'.format(signer_address))
signer = EIP155Signer(keystore)
chain_spec = ChainSpec.from_chain_str(args.i)
chain_id = chain_spec.network_id()
-helper = EthTxExecutor(
- w3,
- signer_address,
- signer,
- chain_id,
- block=args.ww,
- )
+rpc = EthHTTPConnection(args.p)
+nonce_oracle = RPCNonceOracle(signer_address, rpc)
+gas_oracle = RPCGasOracle(rpc, code_callback=AccountRegistry.gas)
def main():
- f = open(os.path.join(args.abi_dir, 'AccountsIndex.json'), 'r')
- abi = json.load(f)
- f.close()
-
- f = open(os.path.join(args.abi_dir, 'AccountsIndex.bin'), 'r')
- bytecode = f.read()
- f.close()
-
- c = w3.eth.contract(abi=abi, bytecode=bytecode)
- (tx_hash, rcpt) = helper.sign_and_send(
- [
- c.constructor().buildTransaction
- ],
- force_wait=True,
- )
- address = rcpt.contractAddress
-
- c = w3.eth.contract(abi=abi, address=address)
-
- logg.debug('adding sender to write list')
- (tx_hash, rcpt) = helper.sign_and_send(
- [
- c.functions.addWriter(signer_address).buildTransaction,
- ],
- force_wait=True,
- )
-
- if args.writer != None:
- for w in args.writer:
- logg.info('adding {} to write list'.format(w))
- (tx_hash, rcpt) = helper.sign_and_send(
- [
- c.functions.addWriter(w).buildTransaction
- ],
- )
-
- if args.account != None:
- for a in args.account:
- logg.info('adding {} to accounts index'.format(a))
- (tx_hash, rcpt) = helper.sign_and_send(
- [
- c.functions.add(a).buildTransaction
- ],
- )
-
- if not args.keep_sender:
- logg.debug('deleting sender for write list')
- (tx_hash, rcpt) = helper.sign_and_send(
- [
- c.functions.deleteWriter(signer_address).buildTransaction,
- ],
- )
-
- if block_last or block_all:
- helper.wait_for()
-
- print(address)
+ c = AccountRegistry(signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=chain_id)
+ (tx_hash_hex, o) = c.constructor(signer_address)
+ rpc.do(o)
+ if block_last:
+ r = rpc.wait(tx_hash_hex)
+ if r['status'] == 0:
+ sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you')
+ sys.exit(1)
+ # TODO: pass through translator for keys (evm tester uses underscore instead of camelcase)
+ address = r['contractAddress']
+
+ print(address)
+ else:
+ print(tx_hash_hex)
+
+ sys.exit(0)
if __name__ == '__main__':
diff --git a/python/eth_accounts_index/runnable/writer.py b/python/eth_accounts_index/runnable/writer.py
@@ -0,0 +1,103 @@
+"""Adds/removes writers to accounts index
+
+.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
+.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746
+
+"""
+
+# standard imports
+import os
+import json
+import argparse
+import logging
+import sys
+
+# third-party imports
+from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
+from crypto_dev_signer.keystore.dict import DictKeystore
+from chainlib.chain import ChainSpec
+from chainlib.eth.nonce import RPCNonceOracle
+from chainlib.eth.gas import RPCGasOracle
+from chainlib.eth.connection import EthHTTPConnection
+from chainlib.eth.tx import receipt
+
+# local imports
+from eth_accounts_index import AccountRegistry
+
+logging.basicConfig(level=logging.WARNING)
+logg = logging.getLogger()
+
+script_dir = os.path.dirname(__file__)
+data_dir = os.path.join(script_dir, '..', 'data')
+
+argparser = argparse.ArgumentParser()
+argparser.add_argument('-p', '--provider', dest='p', default='http://localhost:8545', type=str, help='Web3 provider url (http only)')
+argparser.add_argument('-w', action='store_true', help='Wait for the last transaction to be confirmed')
+argparser.add_argument('-ww', action='store_true', help='Wait for every transaction to be confirmed')
+argparser.add_argument('-i', '--chain-spec', dest='i', type=str, default='Ethereum:1', help='Chain specification string')
+argparser.add_argument('-y', '--key-file', dest='y', type=str, help='Ethereum keystore file to use for signing')
+argparser.add_argument('-a', '--contract-address', dest='a', required=True, type=str, help='Contract address to account index to edit')
+argparser.add_argument('--delete', action='store_true', help='Delete address')
+argparser.add_argument('-v', action='store_true', help='Be verbose')
+argparser.add_argument('-vv', action='store_true', help='Be more verbose')
+argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration')
+argparser.add_argument('address', type=str, help='Subject address')
+args = argparser.parse_args()
+
+if args.vv:
+ logg.setLevel(logging.DEBUG)
+elif args.v:
+ logg.setLevel(logging.INFO)
+
+block_last = args.w
+block_all = args.ww
+
+passphrase_env = 'ETH_PASSPHRASE'
+if args.env_prefix != None:
+ passphrase_env = args.env_prefix + '_' + passphrase_env
+passphrase = os.environ.get(passphrase_env)
+if passphrase == None:
+ logg.warning('no passphrase given')
+ passphrase=''
+
+signer_address = None
+keystore = DictKeystore()
+if args.y != None:
+ logg.debug('loading keystore file {}'.format(args.y))
+ signer_address = keystore.import_keystore_file(args.y, password=passphrase)
+ logg.debug('now have key for signer address {}'.format(signer_address))
+signer = EIP155Signer(keystore)
+
+chain_spec = ChainSpec.from_chain_str(args.i)
+chain_id = chain_spec.network_id()
+
+rpc = EthHTTPConnection(args.p)
+nonce_oracle = RPCNonceOracle(signer_address, rpc)
+gas_oracle = RPCGasOracle(rpc, code_callback=AccountRegistry.gas)
+contract_address = args.a
+address = args.address
+delete = False
+if args.delete:
+ delete = True
+
+
+def main():
+ c = AccountRegistry(signer=signer, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=chain_id)
+ tx_hash_hex = None
+ o = None
+ if delete:
+ (tx_hash_hex, o) = c.delete_writer(contract_address, signer_address, address)
+ else:
+ (tx_hash_hex, o) = c.add_writer(contract_address, signer_address, address)
+ rpc.do(o)
+ r = rpc.wait(tx_hash_hex)
+ if block_last:
+ if r['status'] == 0:
+ sys.stderr.write('EVM revert while deploying contract. Wish I had more to tell you')
+ sys.exit(1)
+
+ print(tx_hash_hex)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/python/requirements.txt b/python/requirements.txt
@@ -1,3 +1,3 @@
-confini~=0.3.6rc1
-web3==5.12.2
-chainlib~=0.0.1a16
+confini~=0.3.6rc3
+chainlib~=0.0.1a42
+crypto-dev-signer~=0.4.14a15
diff --git a/python/setup.cfg b/python/setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = eth-accounts-index
-version = 0.0.10a10
+version = 0.0.11a6
description = Accounts index evm contract tooling with permissioned writes
author = Louis Holbrook
author_email = dev@holbrook.no
@@ -26,15 +26,6 @@ python_requires = >= 3.6
packages =
eth_accounts_index
eth_accounts_index.runnable
-install_requires =
- confini~=0.3.6a1
- web3==5.12.2
- crypto-dev-signer~=0.4.13rc2
- chainlib~=0.0.1a16
-tests_require =
- pytest==6.0.1
- eth-tester==0.5.0b2
- py-evm==0.3.0a20
[options.extras_require]
testing =
@@ -51,3 +42,5 @@ testing =
console_scripts =
eth-accounts-index-deploy = eth_accounts_index.runnable.deploy:main
eth-accounts-index-add = eth_accounts_index.runnable.add:main
+ eth-accounts-index-writer = eth_accounts_index.runnable.writer:main
+ eth-accounts-index-list = eth_accounts_index.runnable.list:main
diff --git a/python/setup.py b/python/setup.py
@@ -1,5 +1,24 @@
from setuptools import setup
+requirements = []
+f = open('requirements.txt', 'r')
+while True:
+ l = f.readline()
+ if l == '':
+ break
+ requirements.append(l.rstrip())
+f.close()
+
+test_requirements = []
+f = open('test_requirements.txt', 'r')
+while True:
+ l = f.readline()
+ if l == '':
+ break
+ test_requirements.append(l.rstrip())
+f.close()
+
+
setup(
package_data={
'': [
@@ -8,4 +27,6 @@ setup(
],
},
include_package_data=True,
+ install_requires=requirements,
+ tests_require=test_requirements,
)
diff --git a/python/test_requirements.txt b/python/test_requirements.txt
@@ -0,0 +1,2 @@
+eth_tester==0.5.0b3
+py-evm==0.3.0a20
diff --git a/python/tests/test_app.py b/python/tests/test_app.py
@@ -1,11 +1,24 @@
+# standard imports
import os
import unittest
import json
import logging
-import web3
-import eth_tester
-
+# external imports
+from chainlib.eth.unittest.ethtester import EthTesterCase
+from chainlib.connection import RPCConnection
+from chainlib.eth.address import to_checksum_address
+from chainlib.eth.tx import (
+ receipt,
+ transaction,
+ TxFormat,
+ )
+from chainlib.eth.contract import (
+ abi_decode_single,
+ ABIContractType,
+ )
+
+# local imports
from eth_accounts_index import AccountRegistry
logging.basicConfig(level=logging.DEBUG)
@@ -14,106 +27,138 @@ logg = logging.getLogger()
testdir = os.path.dirname(__file__)
-class Test(unittest.TestCase):
-
- def setUp(self):
- eth_params = eth_tester.backends.pyevm.main.get_default_genesis_params({
- 'gas_limit': 9000000,
- })
-
- f = open(os.path.join(testdir, '../eth_accounts_index/data/AccountsIndex.bin'), 'r')
- bytecode = f.read()
- f.close()
-
- f = open(os.path.join(testdir, '../eth_accounts_index/data/AccountsIndex.abi.json'), 'r')
- abi = json.load(f)
- f.close()
-
- backend = eth_tester.PyEVMBackend(eth_params)
- self.eth_tester = eth_tester.EthereumTester(backend)
- provider = web3.Web3.EthereumTesterProvider(self.eth_tester)
- self.w3 = web3.Web3(provider)
- c = self.w3.eth.contract(abi=abi, bytecode=bytecode)
- tx_hash = c.constructor().transact({'from': self.w3.eth.accounts[0]})
-
- r = self.w3.eth.getTransactionReceipt(tx_hash)
-
- self.address = r.contractAddress
- c = self.w3.eth.contract(abi=abi, address=self.address)
-
- c.functions.addWriter(self.w3.eth.accounts[1]).transact()
-
-
- def tearDown(self):
- pass
-
-
- def test_basic(self):
- registry = AccountRegistry(self.w3, self.address)
- self.assertEqual(registry.count(), 1); # count starts at 1, first addess is always 0x0
+class TestNonceOracle:
+ def __init__(self, address, default_value=0):
+ self.nonce = default_value
- def test_access(self):
- registry = AccountRegistry(self.w3, self.address, self.w3.eth.accounts[1])
- registry.add(self.w3.eth.accounts[2])
- self.eth_tester.mine_block()
- self.assertEqual(registry.count(), 2)
- # account 2 does not have access
- registry = AccountRegistry(self.w3, self.address, self.w3.eth.accounts[2])
- registry.add(self.w3.eth.accounts[2])
- self.eth_tester.mine_block()
- self.assertEqual(registry.count(), 2)
+ def next_nonce(self):
+ nonce = self.nonce
+ self.nonce += 1
+ return nonce
- # after this account 2 has access
- registry.contract.functions.addWriter(self.w3.eth.accounts[2]).transact()
- registry.add(self.w3.eth.accounts[3])
- self.eth_tester.mine_block()
- self.assertEqual(registry.count(), 3)
- # after this account 2 no longer has access
- registry.contract.functions.deleteWriter(self.w3.eth.accounts[2]).transact()
- registry.add(self.w3.eth.accounts[3])
- self.eth_tester.mine_block()
- self.assertEqual(registry.count(), 3)
-
-
- def test_indices(self):
- registry = AccountRegistry(self.w3, self.address, self.w3.eth.accounts[1])
- registry.add(self.w3.eth.accounts[2])
-
- self.assertTrue(registry.have(self.w3.eth.accounts[2]))
- self.assertFalse(registry.have(self.w3.eth.accounts[3]))
-
-
- def test_no_duplicates(self):
- registry = AccountRegistry(self.w3, self.address, self.w3.eth.accounts[1])
- tx_hash = registry.add(self.w3.eth.accounts[2])
- self.eth_tester.mine_block()
- tx_hash = registry.add(self.w3.eth.accounts[3])
- self.eth_tester.mine_block()
- # BUG: eth_tester does not detect the duplicate here, but does in the test.py file in the solidity folder
- #self.assertRaises(Exception):
- tx_hash = registry.add(self.w3.eth.accounts[2])
- self.eth_tester.mine_block()
-
-
- def test_list(self):
- registry = AccountRegistry(self.w3, self.address, self.w3.eth.accounts[1])
-
- for i in range(2, 10):
- registry.add(self.w3.eth.accounts[i])
-
- self.assertEqual(registry.count(), 9)
-
- accounts_reverse = []
- for i in range(9, 1, -1):
- accounts_reverse.append(self.w3.eth.accounts[i])
-
- accounts_list = registry.last(8)
- for i in range(8):
- self.assertEqual(accounts_list[i], accounts_reverse[i])
+class Test(EthTesterCase):
+ def setUp(self):
+ super(Test, self).setUp()
+ nonce_oracle = TestNonceOracle(self.accounts[0])
+ self.o = AccountRegistry(signer=self.signer, nonce_oracle=nonce_oracle)
+ (tx_hash, o) = self.o.constructor(self.accounts[0])
+ self.conn = RPCConnection.connect(self.chain_spec, 'default')
+ r = self.conn.do(o)
+ logg.debug('deployed with hash {}'.format(r))
+
+ o = receipt(r)
+ r = self.conn.do(o)
+ self.address = to_checksum_address(r['contract_address'])
+
+ (tx_hash, o) = self.o.add_writer(self.address, self.accounts[0], self.accounts[0])
+ r = self.conn.do(o)
+
+ o = receipt(r)
+ r = self.conn.do(o)
+ self.assertEqual(r['status'], 1)
+
+
+ def test_1_count(self):
+ o = self.o.count(self.address, sender_address=self.accounts[0])
+ r = self.conn.do(o)
+ r = abi_decode_single(ABIContractType.UINT256, r)
+ self.assertEqual(r, 1)
+
+
+ def test_2_add(self):
+ b = os.urandom(20)
+ a = to_checksum_address(b.hex())
+
+ (tx_hash, o) = self.o.add(self.address, self.accounts[0], a)
+ r = self.conn.do(o)
+ self.assertEqual(tx_hash, r)
+
+ o = receipt(tx_hash)
+ rcpt = self.conn.do(o)
+
+ self.helper.mine_block()
+ o = self.o.have(self.address, a, sender_address=self.accounts[0])
+ r = self.conn.do(o)
+
+
+ def test_3_add_rlpsigned(self):
+ b = os.urandom(20)
+ a = to_checksum_address(b.hex())
+
+ (tx_hash, o) = self.o.add(self.address, self.accounts[0], a, tx_format=TxFormat.RLP_SIGNED)
+ #r = self.conn.do(o)
+ #self.assertEqual(tx_hash, r)
+ logg.debug('o {}'.format(o))
+
+
+
+
+# TODO: reinstate all tests
+# def test_access(self):
+# registry = AccountRegistry(self.w3, self.address, self.w3.eth.accounts[1])
+# registry.add(self.w3.eth.accounts[2])
+# self.eth_tester.mine_block()
+# self.assertEqual(registry.count(), 2)
+#
+# # account 2 does not have access
+# registry = AccountRegistry(self.w3, self.address, self.w3.eth.accounts[2])
+# registry.add(self.w3.eth.accounts[2])
+# self.eth_tester.mine_block()
+# self.assertEqual(registry.count(), 2)
+#
+# # after this account 2 has access
+# registry.contract.functions.addWriter(self.w3.eth.accounts[2]).transact()
+# registry.add(self.w3.eth.accounts[3])
+# self.eth_tester.mine_block()
+# self.assertEqual(registry.count(), 3)
+#
+# # after this account 2 no longer has access
+# registry.contract.functions.deleteWriter(self.w3.eth.accounts[2]).transact()
+# registry.add(self.w3.eth.accounts[3])
+# self.eth_tester.mine_block()
+# self.assertEqual(registry.count(), 3)
+#
+#
+# def test_indices(self):
+# registry = AccountRegistry(self.w3, self.address, self.w3.eth.accounts[1])
+# registry.add(self.w3.eth.accounts[2])
+#
+# self.assertTrue(registry.have(self.w3.eth.accounts[2]))
+# self.assertFalse(registry.have(self.w3.eth.accounts[3]))
+#
+#
+# def test_no_duplicates(self):
+# registry = AccountRegistry(self.w3, self.address, self.w3.eth.accounts[1])
+# tx_hash = registry.add(self.w3.eth.accounts[2])
+# self.eth_tester.mine_block()
+# tx_hash = registry.add(self.w3.eth.accounts[3])
+# self.eth_tester.mine_block()
+# # BUG: eth_tester does not detect the duplicate here, but does in the test.py file in the solidity folder
+# #self.assertRaises(Exception):
+# tx_hash = registry.add(self.w3.eth.accounts[2])
+# self.eth_tester.mine_block()
+#
+#
+# def test_list(self):
+# registry = AccountRegistry(self.w3, self.address, self.w3.eth.accounts[1])
+#
+# for i in range(2, 10):
+# registry.add(self.w3.eth.accounts[i])
+#
+# self.assertEqual(registry.count(), 9)
+#
+# accounts_reverse = []
+# for i in range(9, 1, -1):
+# accounts_reverse.append(self.w3.eth.accounts[i])
+#
+# accounts_list = registry.last(8)
+# for i in range(8):
+# self.assertEqual(accounts_list[i], accounts_reverse[i])
+#
if __name__ == '__main__':
unittest.main()