commit 689552a05eb3b0d8c587b7bd292b1cd0630e1cb0
parent 020e069fd89401127bd430d57fdd23aa3f6bae2a
Author: nolash <dev@holbrook.no>
Date: Sat, 17 Apr 2021 10:54:10 +0200
Add python interface, port tests to chainlib
Diffstat:
11 files changed, 190 insertions(+), 82 deletions(-)
diff --git a/python/eth_void_owner/__init__.py b/python/eth_void_owner/__init__.py
@@ -0,0 +1,2 @@
+from .interface import VoidOwner
+from eth_void_owner.data import data_dir
diff --git a/python/eth_void_owner/data/Owned.bin b/python/eth_void_owner/data/Owned.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610414806100606000396000f3fe608060405234801561001057600080fd5b506004361061005e576000357c01000000000000000000000000000000000000000000000000000000009004806379ba5097146100635780638da5cb5b14610081578063f2fde38b1461009f575b600080fd5b61006b6100cf565b604051610078919061036e565b60405180910390f35b610089610233565b6040516100969190610353565b60405180910390f35b6100b960048036038101906100b4919061030c565b610257565b6040516100c6919061036e565b60405180910390f35b6000803373ffffffffffffffffffffffffffffffffffffffff16600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461012c57600080fd5b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3600191505090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003373ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146102b157600080fd5b81600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550919050565b600081359050610306816103c7565b92915050565b60006020828403121561031e57600080fd5b600061032c848285016102f7565b91505092915050565b61033e81610389565b82525050565b61034d8161039b565b82525050565b60006020820190506103686000830184610335565b92915050565b60006020820190506103836000830184610344565b92915050565b6000610394826103a7565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6103d081610389565b81146103db57600080fd5b5056fea26469706673582212209256ba8fb1008f23c6c54c9f58c19fe1f026dad98aed5bc9de922fff6f4f1be764736f6c63430008030033
+\ No newline at end of file
diff --git a/python/eth_void_owner/data/Owned.json b/python/eth_void_owner/data/Owned.json
@@ -0,0 +1 @@
+[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
diff --git a/python/eth_void_owner/data/VoidOwner.bin b/python/eth_void_owner/data/VoidOwner.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b50610676806100206000396000f3fe608060405234801561001057600080fd5b5060043610610048576000357c010000000000000000000000000000000000000000000000000000000090048063295a75371461004d575b600080fd5b610067600480360381019061006291906103a2565b61007d565b60405161007491906104bb565b60405180910390f35b600080606060008473ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527f79ba5097000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161012b9190610489565b6000604051808303816000865af19150503d8060008114610168576040519150601f19603f3d011682016040523d82523d6000602084013e61016d565b606091505b508093508194505050826101b6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101ad906104d6565b60405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527f8da5cb5b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161025d9190610489565b6000604051808303816000865af19150503d806000811461029a576040519150601f19603f3d011682016040523d82523d6000602084013e61029f565b606091505b508093508194505050826102e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102df906104f6565b60405180910390fd5b818060200190518101906102fc91906103cb565b90508073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff161461033657600080fd5b7fdc3c82f4776932041f15a08f769aadd6ed44c2a975e64bbf0fde8cf812f8b6b88560405161036591906104a0565b60405180910390a1829350505050919050565b60008135905061038781610612565b92915050565b60008151905061039c81610629565b92915050565b6000602082840312156103b457600080fd5b60006103c284828501610378565b91505092915050565b6000602082840312156103dd57600080fd5b60006103eb8482850161038d565b91505092915050565b6103fd8161053d565b82525050565b61040c81610561565b82525050565b600061041d82610516565b6104278185610521565b935061043781856020860161058d565b80840191505092915050565b6000610450600a8361052c565b915061045b826105c0565b602082019050919050565b6000610473600d8361052c565b915061047e826105e9565b602082019050919050565b60006104958284610412565b915081905092915050565b60006020820190506104b560008301846103f4565b92915050565b60006020820190506104d06000830184610403565b92915050565b600060208201905081810360008301526104ef81610443565b9050919050565b6000602082019050818103600083015261050f81610466565b9050919050565b600081519050919050565b600081905092915050565b600082825260208201905092915050565b60006105488261056d565b9050919050565b600061055a8261056d565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60005b838110156105ab578082015181840152602081019050610590565b838111156105ba576000848401525b50505050565b7f4552525f41434345505400000000000000000000000000000000000000000000600082015250565b7f4552525f494e5445524641434500000000000000000000000000000000000000600082015250565b61061b8161053d565b811461062657600080fd5b50565b6106328161054f565b811461063d57600080fd5b5056fea264697066735822122011c0a1dbbda64501370d85ab3d0ff50c4e0167db06d034c5230fb47d13e0de0a64736f6c63430008030033
+\ No newline at end of file
diff --git a/python/eth_void_owner/data/VoidOwner.json b/python/eth_void_owner/data/VoidOwner.json
@@ -0,0 +1 @@
+[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_result","type":"address"}],"name":"OwnershipTaken","type":"event"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"omNom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
diff --git a/python/eth_void_owner/data/__init__.py b/python/eth_void_owner/data/__init__.py
@@ -0,0 +1,4 @@
+# standard imports
+import os
+
+data_dir = os.path.realpath(os.path.dirname(__file__))
diff --git a/python/eth_void_owner/interface.py b/python/eth_void_owner/interface.py
@@ -0,0 +1,77 @@
+# 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 VoidOwner(TxFactory):
+
+ __abi = None
+ __bytecode = None
+
+ @staticmethod
+ def abi():
+ if VoidOwner.__abi == None:
+ f = open(os.path.join(datadir, 'VoidOwner.json'), 'r')
+ VoidOwner.__abi = json.load(f)
+ f.close()
+ return VoidOwner.__abi
+
+
+ @staticmethod
+ def bytecode():
+ if VoidOwner.__bytecode == None:
+ f = open(os.path.join(datadir, 'VoidOwner.bin'))
+ VoidOwner.__bytecode = f.read()
+ f.close()
+ return VoidOwner.__bytecode
+
+
+ @staticmethod
+ def gas(code=None):
+ return 700000
+
+
+ def constructor(self, sender_address):
+ code = VoidOwner.bytecode()
+ tx = self.template(sender_address, None, use_nonce=True)
+ tx = self.set_code(tx, code)
+ return self.build(tx)
+
+
+ def take_ownership(self, sender_address, contract_address, address, tx_format=TxFormat.JSONRPC):
+ enc = ABIContractEncoder()
+ enc.method('omNom')
+ 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
diff --git a/python/requirements.txt b/python/requirements.txt
@@ -0,0 +1,3 @@
+confini~=0.3.6rc3
+chainlib~=0.0.2a12
+crypto-dev-signer~=0.4.14b2
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_basic.py b/python/tests/test_basic.py
@@ -4,97 +4,111 @@ import unittest
import json
import logging
-# third-party imports
-import web3
-import eth_tester
-import eth_abi
+# 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.nonce import RPCNonceOracle
+from chainlib.eth.tx import (
+ receipt,
+ transaction,
+ TxFormat,
+ TxFactory,
+ )
+from chainlib.eth.contract import (
+ abi_decode_single,
+ ABIContractType,
+ )
+from chainlib.jsonrpc import jsonrpc_template
+from chainlib.eth.contract import (
+ ABIContractEncoder,
+ )
+from hexathon import add_0x
+
+# local imports
+from eth_void_owner import (
+ VoidOwner,
+ data_dir,
+ )
logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
-logging.getLogger('web3').setLevel(logging.WARNING)
-logging.getLogger('eth.vm').setLevel(logging.WARNING)
-
testdir = os.path.dirname(__file__)
-class Test(unittest.TestCase):
+class Test(EthTesterCase):
- contract = None
def setUp(self):
- eth_params = eth_tester.backends.pyevm.main.get_default_genesis_params({
- 'gas_limit': 9000000,
- })
-
- f = open(os.path.join(testdir, '../../solidity/Owned.bin'), 'r')
- self.bytecode_owned = f.read()
+ super(Test, self).setUp()
+ self.conn = RPCConnection.connect(self.chain_spec, 'default')
+ nonce_oracle = RPCNonceOracle(self.accounts[0], self.conn)
+ self.o = VoidOwner(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
+ (tx_hash_hex, o) = self.o.constructor(self.accounts[0])
+ r = self.conn.do(o)
+ logg.debug('deployed with hash {}'.format(r))
+
+ o = receipt(tx_hash_hex)
+ r = self.conn.do(o)
+ self.address = r['contract_address']
+
+ f = open(os.path.join(data_dir, 'Owned.bin'), 'r')
+ b = f.read()
f.close()
-
- f = open(os.path.join(testdir, '../../solidity/Owned.json'), 'r')
- self.abi_owned = json.load(f)
- f.close()
-
- f = open(os.path.join(testdir, '../../solidity/VoidOwner.bin'), 'r')
- self.bytecode_void = f.read()
- f.close()
-
- f = open(os.path.join(testdir, '../../solidity/VoidOwner.json'), 'r')
- self.abi_void = 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=self.abi_owned, bytecode=self.bytecode_owned)
- tx_hash = c.constructor().transact()
- r = self.w3.eth.getTransactionReceipt(tx_hash)
- self.assertEqual(r.status, 1)
- self.contract_owned = self.w3.eth.contract(abi=self.abi_owned, address=r.contractAddress)
-
- c = self.w3.eth.contract(abi=self.abi_void, bytecode=self.bytecode_void)
- tx_hash = c.constructor().transact()
- r = self.w3.eth.getTransactionReceipt(tx_hash)
- self.assertEqual(r.status, 1)
- self.contract_void = self.w3.eth.contract(abi=self.abi_void, address=r.contractAddress)
-
-
- def tearDown(self):
- pass
-
-
- def test_hello(self):
- owner = self.contract_owned.functions.owner().call()
- self.assertEqual(self.w3.eth.accounts[0], owner)
-
-
- def test_accept(self):
- owner = self.contract_owned.functions.owner().call()
-
- tx_hash = self.contract_owned.functions.transferOwnership(self.contract_void.address).transact()
- r = self.w3.eth.getTransactionReceipt(tx_hash)
- self.assertEqual(r.status, 1)
-
- tx_hash = self.contract_void.functions.omNom(self.contract_owned.address).transact()
- r = self.w3.eth.getTransactionReceipt(tx_hash)
- self.assertEqual(r.status, 1)
- logged = False
- for l in r.logs:
- logg.debug('log {}'.format(l))
- if l.topics[0].hex() == '0xdc3c82f4776932041f15a08f769aadd6ed44c2a975e64bbf0fde8cf812f8b6b8':
- matchLogAddress = '0x{:>064}'.format(self.contract_owned.address[2:].lower()
- self.assertEqual(l.data, matchLogAddress))
- logged = True
-
- self.assertTrue(logged)
-
- owner = self.contract_owned.functions.owner().call()
- self.assertEqual(owner, self.contract_void.address)
-
- tx = self.contract_owned.functions.transferOwnership(self.contract_void.address)
- self.assertRaises(eth_tester.exceptions.TransactionFailed, tx.transact)
+
+ txf = TxFactory(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
+ tx = txf.template(self.accounts[0], None, use_nonce=True)
+ tx = txf.set_code(tx, b)
+ (tx_hash_hex, o) = txf.build(tx)
+ r = self.conn.do(o)
+
+ o = receipt(tx_hash_hex)
+ r = self.conn.do(o)
+ self.owned_demo_address = r['contract_address']
+
+
+ def test_takeover(self):
+
+ nonce_oracle = RPCNonceOracle(self.accounts[0], self.conn)
+ txf = TxFactory(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
+
+ enc = ABIContractEncoder()
+ enc.method('transferOwnership')
+ enc.typ(ABIContractType.ADDRESS)
+ enc.address(self.address)
+ data = enc.get()
+ tx = txf.template(self.accounts[0], self.owned_demo_address, use_nonce=True)
+ tx = txf.set_code(tx, data)
+ (tx_hash_hex, o) = txf.finalize(tx)
+
+ r = self.conn.do(o)
+
+ o = receipt(tx_hash_hex)
+ r = self.conn.do(o)
+ self.assertEqual(r['status'], 1)
+
+ c = VoidOwner(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
+ (tx_hash_hex, o) = c.take_ownership(self.accounts[0], self.address, self.owned_demo_address)
+
+ r = self.conn.do(o)
+
+ o = receipt(tx_hash_hex)
+ r = self.conn.do(o)
+ self.assertEqual(r['status'], 1)
+
+ o = jsonrpc_template()
+ o['method'] = 'eth_call'
+ enc = ABIContractEncoder()
+ enc.method('owner')
+ data = add_0x(enc.get())
+ tx = txf.template(self.accounts[0], self.owned_demo_address)
+ tx = txf.set_code(tx, data)
+ o['params'].append(txf.normalize(tx))
+
+ r = self.conn.do(o)
+ owner_address = abi_decode_single(ABIContractType.ADDRESS, r)
+ self.assertEqual(owner_address, self.address)
if __name__ == '__main__':
diff --git a/solidity/Makefile b/solidity/Makefile
@@ -12,7 +12,7 @@ test: all
python ../python/tests/test_basic.py
install: all
- cp -v VoidOwner.{json,bin} ../python/void_owner/data/
- cp -v Owned.{json,bin} ../python/void_owner/data/
+ cp -v VoidOwner.{json,bin} ../python/eth_void_owner/data/
+ cp -v Owned.{json,bin} ../python/eth_void_owner/data/
.PHONY: test install