From 22544ceb232b60b74f9dc6c10ac4a2a3b75e6b01 Mon Sep 17 00:00:00 2001 From: nolash Date: Fri, 13 Nov 2020 23:19:48 +0100 Subject: [PATCH] Add python support, move test to python unittest --- .gitignore | 2 + python/eth_accounts_index/__init__.py | 1 + .../eth_accounts_index/data/registry.abi.json | 0 python/eth_accounts_index/data/registry.bin | 1 + python/eth_accounts_index/registry.py | 55 ++++++++++ python/setup.cfg | 28 +++++ python/setup.py | 6 + python/tests/test_app.py | 103 ++++++++++++++++++ registry.bin | 1 - solidity/registry.abi.json | 1 + solidity/registry.bin | 1 + registry.sol => solidity/registry.sol | 2 + test.py | 42 ------- 13 files changed, 200 insertions(+), 43 deletions(-) create mode 100644 .gitignore create mode 100644 python/eth_accounts_index/__init__.py rename registry.abi.json => python/eth_accounts_index/data/registry.abi.json (100%) create mode 100644 python/eth_accounts_index/data/registry.bin create mode 100644 python/eth_accounts_index/registry.py create mode 100644 python/setup.cfg create mode 100644 python/setup.py create mode 100644 python/tests/test_app.py delete mode 100644 registry.bin create mode 100644 solidity/registry.abi.json create mode 100644 solidity/registry.bin rename registry.sol => solidity/registry.sol (94%) delete mode 100644 test.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d35cb3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +*.pyc diff --git a/python/eth_accounts_index/__init__.py b/python/eth_accounts_index/__init__.py new file mode 100644 index 0000000..f9691f6 --- /dev/null +++ b/python/eth_accounts_index/__init__.py @@ -0,0 +1 @@ +from .registry import Registry diff --git a/registry.abi.json b/python/eth_accounts_index/data/registry.abi.json similarity index 100% rename from registry.abi.json rename to python/eth_accounts_index/data/registry.abi.json diff --git a/python/eth_accounts_index/data/registry.bin b/python/eth_accounts_index/data/registry.bin new file mode 100644 index 0000000..c23b607 --- /dev/null +++ b/python/eth_accounts_index/data/registry.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b5033600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160028190555061054f806100cb6000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c806306661abd146100675780630a3b0a4f146100855780634c2ebc6b146100c95780635ae06f7e14610121578063da2824a814610165578063f2a40db8146101a9575b600080fd5b61006f610201565b6040518082815260200191505060405180910390f35b6100c76004803603602081101561009b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610207565b005b61010b600480360360208110156100df57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610364565b6040518082815260200191505060405180910390f35b6101636004803603602081101561013757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061037c565b005b6101a76004803603602081101561017b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610428565b005b6101d5600480360360208110156101bf57600080fd5b81019080803590602001909291905050506104dd565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60025481565b600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1661025d57600080fd5b6000819080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600254600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506002600081548092919060010191905055506001600254038173ffffffffffffffffffffffffffffffffffffffff167f5ed3bdd47b9af629827a8d129aa39c870b10c03f0153fe9ddb8e84b665061acd60405160405180910390a350565b60016020528060005260406000206000915090505481565b3373ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146103d657600080fd5b600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81549060ff021916905550565b3373ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461048257600080fd5b6001600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b600081815481106104ea57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff168156fea26469706673582212204dc3c533897e9c630d33c8c0767e5393cf2e4f0faca5d0cbc39dbe82580e869564736f6c63430007030033 \ No newline at end of file diff --git a/python/eth_accounts_index/registry.py b/python/eth_accounts_index/registry.py new file mode 100644 index 0000000..1048e0d --- /dev/null +++ b/python/eth_accounts_index/registry.py @@ -0,0 +1,55 @@ +import logging +import json +import os + +logg = logging.getLogger() + +class Registry: + + abi = None + + def __init__(self, w3, address, signer_address=None): + if Registry.abi == None: + moddir = os.path.dirname(__file__) + datadir = os.path.join(moddir, 'data') + f = open(os.path.join(datadir, 'registry.abi.json'), 'r') + Registry.abi = json.load(f) + f.close() + self.contract = w3.eth.contract(abi=Registry.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 + + + def add(self, address): + gasPrice = self.w3.eth.gasPrice; + tx_hash = self.contract.functions.add(address).transact({ + 'gasPrice': gasPrice, + 'gas': 100000, + 'from': self.signer_address, + }) + + + def count(self): + return self.contract.functions.count().call() + + + def have(self, address): + r = self.contract.functions.accountsIndex(address).call() + return r != 0 + + + 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 diff --git a/python/setup.cfg b/python/setup.cfg new file mode 100644 index 0000000..3ffe10a --- /dev/null +++ b/python/setup.cfg @@ -0,0 +1,28 @@ +[metadata] +name = accounts-index +version = 0.0.1 +description = Accounts index evm contract tooling with permissioned writes +author = Louis Holbrook +author_email = dev@holbrook.no +url = https://gitlab.com/nolash/eth-accounts-index +keywords = + ethereum +classifiers = + Programming Language :: Python :: 3 + Operating System :: OS Independent + Development Status :: 3 - Alpha + Environment :: No Input/Output (Daemon) + Intended Audience :: Developers + License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) + Topic :: Internet + #Topic :: Blockchain :: EVM +license = GPL3 +licence_files = + LICENSE.txt + +[options] +python_requires = >= 3.6 +packages = +install_requires = + confini==0.2.1 + web3==5.12.2 diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 0000000..9ece291 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,6 @@ +from setuptools import setup + +setup( + scripts=[ + ] + ) diff --git a/python/tests/test_app.py b/python/tests/test_app.py new file mode 100644 index 0000000..66772f4 --- /dev/null +++ b/python/tests/test_app.py @@ -0,0 +1,103 @@ +import os +import unittest +import json + +import web3 +import eth_tester + +from eth_accounts_index import Registry + +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/registry.bin'), 'r') + bytecode = f.read() + f.close() + + f = open(os.path.join(testdir, '../eth_accounts_index/data/registry.abi.json'), 'r') + abi = json.load(f) + f.close() + + backend = eth_tester.PyEVMBackend(eth_params) + self.eth_tester = instance = eth_tester.EthereumTester(backend) + provider = web3.Web3.EthereumTesterProvider(instance) + 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 = Registry(self.w3, self.address) + self.assertEqual(registry.count(), 1); # count starts at 1, first addess is always 0x0 + + + def test_access(self): + registry = Registry(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 0 does not have access + registry = Registry(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 = Registry(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_list(self): + registry = Registry(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() diff --git a/registry.bin b/registry.bin deleted file mode 100644 index 82dbdf4..0000000 --- a/registry.bin +++ /dev/null @@ -1 +0,0 @@ -608060405234801561001057600080fd5b5033600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061054f806100616000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c806306661abd146100675780630a3b0a4f146100855780634c2ebc6b146100c95780635ae06f7e14610121578063da2824a814610165578063f2a40db8146101a9575b600080fd5b61006f610201565b6040518082815260200191505060405180910390f35b6100c76004803603602081101561009b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610207565b005b61010b600480360360208110156100df57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610364565b6040518082815260200191505060405180910390f35b6101636004803603602081101561013757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061037c565b005b6101a76004803603602081101561017b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610428565b005b6101d5600480360360208110156101bf57600080fd5b81019080803590602001909291905050506104dd565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60025481565b600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1661025d57600080fd5b6000819080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600254600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506002600081548092919060010191905055506001600254038173ffffffffffffffffffffffffffffffffffffffff167f5ed3bdd47b9af629827a8d129aa39c870b10c03f0153fe9ddb8e84b665061acd60405160405180910390a350565b60016020528060005260406000206000915090505481565b3373ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146103d657600080fd5b600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81549060ff021916905550565b3373ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461048257600080fd5b6001600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b600081815481106104ea57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff168156fea2646970667358221220d0c43ebb44ba788ac3723dd1aa1293f073f92c8f6e68a8dd975553f67976555264736f6c63430007030033 \ No newline at end of file diff --git a/solidity/registry.abi.json b/solidity/registry.abi.json new file mode 100644 index 0000000..611f3db --- /dev/null +++ b/solidity/registry.abi.json @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addedAccount","type":"address"},{"indexed":true,"internalType":"uint256","name":"accountIndex","type":"uint256"}],"name":"AccountAdded","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"accounts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"accountsIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"add","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_writer","type":"address"}],"name":"addWriter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"count","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_writer","type":"address"}],"name":"deleteWriter","outputs":[],"stateMutability":"nonpayable","type":"function"}] diff --git a/solidity/registry.bin b/solidity/registry.bin new file mode 100644 index 0000000..c23b607 --- /dev/null +++ b/solidity/registry.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b5033600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160028190555061054f806100cb6000396000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c806306661abd146100675780630a3b0a4f146100855780634c2ebc6b146100c95780635ae06f7e14610121578063da2824a814610165578063f2a40db8146101a9575b600080fd5b61006f610201565b6040518082815260200191505060405180910390f35b6100c76004803603602081101561009b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610207565b005b61010b600480360360208110156100df57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610364565b6040518082815260200191505060405180910390f35b6101636004803603602081101561013757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061037c565b005b6101a76004803603602081101561017b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610428565b005b6101d5600480360360208110156101bf57600080fd5b81019080803590602001909291905050506104dd565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60025481565b600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1661025d57600080fd5b6000819080600181540180825580915050600190039060005260206000200160009091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600254600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506002600081548092919060010191905055506001600254038173ffffffffffffffffffffffffffffffffffffffff167f5ed3bdd47b9af629827a8d129aa39c870b10c03f0153fe9ddb8e84b665061acd60405160405180910390a350565b60016020528060005260406000206000915090505481565b3373ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146103d657600080fd5b600360008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81549060ff021916905550565b3373ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461048257600080fd5b6001600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b600081815481106104ea57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff168156fea26469706673582212204dc3c533897e9c630d33c8c0767e5393cf2e4f0faca5d0cbc39dbe82580e869564736f6c63430007030033 \ No newline at end of file diff --git a/registry.sol b/solidity/registry.sol similarity index 94% rename from registry.sol rename to solidity/registry.sol index ebc84d3..a477a82 100644 --- a/registry.sol +++ b/solidity/registry.sol @@ -14,6 +14,8 @@ contract CustodialAccountIndex { constructor() { owner = msg.sender; + accounts.push(address(0)); + count = 1; } function addWriter(address _writer) public { diff --git a/test.py b/test.py deleted file mode 100644 index 57fce01..0000000 --- a/test.py +++ /dev/null @@ -1,42 +0,0 @@ -import web3 -import eth_tester -import json - - -eth_params = eth_tester.backends.pyevm.main.get_default_genesis_params({ - 'gas_limit': 9000000, - }) -backend = eth_tester.PyEVMBackend(eth_params) -instance = eth_tester.EthereumTester(backend) -provider = web3.Web3.EthereumTesterProvider(instance) -w3 = web3.Web3(provider) - - -f = open('registry.bin', 'r') -bytecode = f.read() -f.close() - -f = open('registry.abi.json', 'r') -abi = json.load(f) -f.close() - -c = w3.eth.contract(abi=abi, bytecode=bytecode) -tx_hash = c.constructor().transact({'from': w3.eth.accounts[0]}) - -r = w3.eth.getTransactionReceipt(tx_hash) - -c = w3.eth.contract(abi=abi, address=r.contractAddress) -fail = False -try: - c.functions.add(w3.eth.accounts[2]).transact({'from': w3.eth.accounts[1]}) -except: - fail = True -assert fail - -c.functions.addWriter(w3.eth.accounts[1]).transact({'from': w3.eth.accounts[0]}) -c.functions.add(w3.eth.accounts[2]).transact({'from': w3.eth.accounts[1]}) -c.functions.add(w3.eth.accounts[3]).transact({'from': w3.eth.accounts[1]}) - -assert c.functions.count().call() == 2 -assert c.functions.accountsIndex(w3.eth.accounts[3]).call() == 1 -assert c.functions.accounts(1).call() == w3.eth.accounts[3]