mirror of https://github.com/nucypher/nucypher.git
167 lines
6.4 KiB
Python
167 lines
6.4 KiB
Python
"""
|
|
This file is part of nucypher.
|
|
|
|
nucypher is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
nucypher is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|
"""
|
|
import json
|
|
import os
|
|
import random
|
|
|
|
import pytest
|
|
from eth_utils import to_canonical_address
|
|
from web3 import EthereumTesterProvider, Web3
|
|
|
|
from nucypher.blockchain.eth.aragon import CallScriptCodec, DAORegistry
|
|
from nucypher.blockchain.eth.constants import DAO_INSTANCES_CONTRACT_TYPE
|
|
from nucypher.blockchain.eth.networks import NetworksInventory
|
|
|
|
|
|
#
|
|
# CallscriptCodec tests
|
|
#
|
|
|
|
@pytest.fixture()
|
|
def mock_web3_contract():
|
|
w3 = Web3(EthereumTesterProvider())
|
|
|
|
foo_abi = dict(constant=False,
|
|
inputs=[],
|
|
name="foo",
|
|
outputs=[],
|
|
payable=False,
|
|
stateMutability="pure",
|
|
type="function")
|
|
|
|
bar_abi = dict(constant=False,
|
|
inputs=[dict(name="", type="address")],
|
|
name="bar",
|
|
outputs=[],
|
|
payable=False,
|
|
stateMutability="pure",
|
|
type="function")
|
|
|
|
abi = [foo_abi, bar_abi]
|
|
contract = w3.eth.contract(abi=abi)
|
|
return contract
|
|
|
|
|
|
def test_callscriptcodec():
|
|
assert CallScriptCodec.CALLSCRIPT_ID == bytes.fromhex("00000001")
|
|
|
|
|
|
def test_callscript_encoding_empty():
|
|
actions = tuple()
|
|
|
|
callscript_data = CallScriptCodec.encode_actions(actions)
|
|
expected_callscript = CallScriptCodec.CALLSCRIPT_ID
|
|
assert expected_callscript == callscript_data
|
|
|
|
|
|
@pytest.mark.parametrize('data_length', range(0, 100, 5))
|
|
def test_callscript_encoding_one_action(get_random_checksum_address, data_length, mock_web3_contract):
|
|
# Action is a byte string
|
|
target = get_random_checksum_address()
|
|
data = os.urandom(data_length)
|
|
actions = [(target, data)]
|
|
|
|
callscript_data = CallScriptCodec.encode_actions(actions)
|
|
expected_callscript = b''.join((CallScriptCodec.CALLSCRIPT_ID,
|
|
to_canonical_address(target),
|
|
data_length.to_bytes(4, 'big'),
|
|
data))
|
|
assert expected_callscript == callscript_data
|
|
|
|
# Action is a hex string
|
|
data = Web3.toHex(data)
|
|
actions = [(target, data)]
|
|
callscript_data = CallScriptCodec.encode_actions(actions)
|
|
assert expected_callscript == callscript_data
|
|
|
|
# Action is a ContractFunction
|
|
function_call = mock_web3_contract.functions.foo()
|
|
actions = [(target, function_call)]
|
|
encoded_foo = Web3.toBytes(hexstr=mock_web3_contract.encodeABI(fn_name="foo"))
|
|
|
|
expected_callscript = b''.join((CallScriptCodec.CALLSCRIPT_ID,
|
|
to_canonical_address(target),
|
|
len(encoded_foo).to_bytes(4, 'big'),
|
|
encoded_foo))
|
|
callscript_data = CallScriptCodec.encode_actions(actions)
|
|
assert expected_callscript == callscript_data
|
|
|
|
|
|
@pytest.mark.parametrize('number_of_actions', range(1, 5))
|
|
def test_callscript_encode_multiple_actions(get_random_checksum_address, number_of_actions):
|
|
actions = [(get_random_checksum_address(), os.urandom(random.randrange(100))) for _ in range(number_of_actions)]
|
|
|
|
callscript_chunks = [CallScriptCodec.CALLSCRIPT_ID]
|
|
for target, data in actions:
|
|
callscript_chunks.extend([to_canonical_address(target), len(data).to_bytes(4, 'big'), data])
|
|
expected_callscript = b''.join(callscript_chunks)
|
|
|
|
callscript_data = CallScriptCodec.encode_actions(actions)
|
|
assert expected_callscript == callscript_data
|
|
|
|
|
|
#
|
|
# DAORegistry tests
|
|
#
|
|
|
|
@pytest.fixture(scope='module')
|
|
def create_mock_dao_registry():
|
|
def _create_mock_dao_registry(path, registry_data):
|
|
filepath = path / DAORegistry._REGISTRY_FILENAME
|
|
path.mkdir(parents=True, exist_ok=True)
|
|
with open(filepath, 'w') as file:
|
|
json.dump(registry_data, file)
|
|
|
|
return filepath
|
|
return _create_mock_dao_registry
|
|
|
|
|
|
def test_dao_registry(tmp_path, create_mock_dao_registry, get_random_checksum_address, mocker):
|
|
NetworksInventory.validate_network_name = lambda _: None
|
|
network = "bananas"
|
|
mocker.patch("nucypher.blockchain.eth.aragon.DAORegistry.get_filepath",
|
|
return_value=tmp_path / network / DAORegistry._REGISTRY_FILENAME)
|
|
|
|
# Let's create some mock registry data
|
|
registry_data = dict()
|
|
for name, app_name in DAO_INSTANCES_CONTRACT_TYPE.items():
|
|
registry_data[name] = dict(app_name=app_name, address=get_random_checksum_address())
|
|
|
|
# Testing normal usage of DAORegistry
|
|
dao_registry_filepath = create_mock_dao_registry(path=tmp_path / network, registry_data=registry_data)
|
|
dao_registry = DAORegistry(network=network)
|
|
assert dao_registry_filepath == dao_registry.filepath
|
|
for name, app_name in DAO_INSTANCES_CONTRACT_TYPE.items():
|
|
assert app_name == dao_registry.get_app_name_of(name)
|
|
assert registry_data[name]['address'] == dao_registry.get_address_of(name)
|
|
|
|
# Testing expected exceptions
|
|
with pytest.raises(ValueError, match="🍌 is not a recognized instance of NuCypherDAO."):
|
|
_ = dao_registry.get_address_of(instance_name="🍌")
|
|
|
|
unknown_address = get_random_checksum_address()
|
|
with pytest.raises(DAORegistry.InstanceNotInRegistry,
|
|
match=f"No instance found in the NuCypherDAO registry with address {unknown_address}"):
|
|
_ = dao_registry.get_instance_name_by_address(unknown_address)
|
|
|
|
# Finding an unknown DAO instance in the registry file should raise an exception
|
|
new_instance = "AgentProvocateur"
|
|
registry_data[new_instance] = dict(app_name="Foo", address=get_random_checksum_address())
|
|
_ = create_mock_dao_registry(path=tmp_path / network, registry_data=registry_data)
|
|
with pytest.raises(ValueError, match="AgentProvocateur is not a recognized instance of NuCypherDAO."):
|
|
_ = DAORegistry(network=network)
|