mirror of https://github.com/nucypher/nucypher.git
[KMS-ETH]- Actor, Contract, and Deployer subclasses for publishing and interfacing ethereum contracts.
parent
f274b3cb4c
commit
d12572546c
|
@ -2,7 +2,6 @@ import random
|
|||
from typing import Set, Generator, List
|
||||
|
||||
from nkms_eth.actors import PolicyAuthor
|
||||
from nkms_eth.base import ContractAgent
|
||||
from nkms_eth.base import EthereumContractAgent
|
||||
from nkms_eth.blockchain import TheBlockchain
|
||||
from nkms_eth.deployers import MinerEscrowDeployer, NuCypherKMSTokenDeployer, PolicyManagerDeployer
|
||||
|
@ -24,7 +23,7 @@ class NuCypherKMSTokenAgent(EthereumContractAgent, deployer=NuCypherKMSTokenDepl
|
|||
return self.call().balanceOf(address)
|
||||
|
||||
|
||||
class MinerAgent(ContractAgent):
|
||||
class MinerAgent(EthereumContractAgent, deployer=MinerEscrowDeployer):
|
||||
"""
|
||||
Wraps NuCypher's Escrow solidity smart contract, and manages a PopulusContract.
|
||||
|
||||
|
@ -33,8 +32,6 @@ class MinerAgent(ContractAgent):
|
|||
for a duration measured in periods.
|
||||
|
||||
"""
|
||||
__deployer = MinerEscrowDeployer
|
||||
_contract_name = __deployer.contract_name
|
||||
|
||||
class NotEnoughUrsulas(Exception):
|
||||
pass
|
||||
|
@ -54,11 +51,11 @@ class MinerAgent(ContractAgent):
|
|||
"""
|
||||
Generates all miner addresses via cumulative sum on-network.
|
||||
"""
|
||||
count = self.call().getMinerInfo(self.MinerInfoField.MINERS_LENGTH.value, self.null_addr, 0).encode('latin-1')
|
||||
count = self.call().getMinerInfo(self._deployer.MinerInfoField.MINERS_LENGTH.value, self._deployer.null_address, 0).encode('latin-1')
|
||||
count = self._blockchain._chain.web3.toInt(count)
|
||||
|
||||
for index in range(count):
|
||||
addr = self.call().getMinerInfo(self.MinerInfoField.MINER.value, self.null_addr, index).encode('latin-1')
|
||||
addr = self.call().getMinerInfo(self._deployer.MinerInfoField.MINER.value, self._deployer.null_address, index).encode('latin-1')
|
||||
yield self._blockchain._chain.web3.toChecksumAddress(addr)
|
||||
|
||||
def sample(self, quantity: int=10, additional_ursulas: float=1.7, attempts: int=5, duration: int=10) -> List[str]:
|
||||
|
@ -95,7 +92,7 @@ class MinerAgent(ContractAgent):
|
|||
points = [0] + sorted(system_random.randrange(n_tokens) for _ in range(n_select))
|
||||
deltas = [i-j for i, j in zip(points[1:], points[:-1])]
|
||||
|
||||
addrs, addr, shift = set(), MinerEscrowDeployer.null_address, 0
|
||||
addrs, addr, shift = set(), MinerEscrowDeployer._null_addr, 0
|
||||
for delta in deltas:
|
||||
addr, shift = self.call().findCumSum(addr, delta+shift, duration)
|
||||
addrs.add(addr)
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
from nkms_eth.blockchain import TheBlockchain
|
||||
|
||||
|
||||
class Actor(ABC):
|
||||
def __init__(self, address):
|
||||
|
@ -17,13 +15,13 @@ class Actor(ABC):
|
|||
|
||||
|
||||
class ContractDeployer(ABC):
|
||||
__contract_name = None
|
||||
_contract_name = NotImplemented
|
||||
|
||||
class ContractDeploymentError(Exception):
|
||||
pass
|
||||
|
||||
def __init__(self, blockchain):
|
||||
self._armed = False
|
||||
self.__armed = False
|
||||
self.__contract = None
|
||||
self._blockchain = blockchain
|
||||
|
||||
|
@ -39,9 +37,12 @@ class ContractDeployer(ABC):
|
|||
return bool(self.__contract is not None)
|
||||
|
||||
@property
|
||||
def is_armed(self) -> bool:
|
||||
return bool(self.__armed is True)
|
||||
|
||||
@classmethod
|
||||
def contract_name(cls) -> str:
|
||||
return cls.__contract_name
|
||||
return cls._contract_name
|
||||
|
||||
def _verify_contract_deployment(self) -> None:
|
||||
"""Raises ContractDeploymentError if the contract has not been armed and deployed."""
|
||||
|
@ -76,25 +77,39 @@ class ContractDeployer(ABC):
|
|||
# return instance
|
||||
|
||||
|
||||
class ContractAgent(ABC):
|
||||
__deployer = None
|
||||
_contract_name = None
|
||||
class EthereumContractAgent(ABC):
|
||||
_deployer = NotImplemented
|
||||
_principal_contract_name = NotImplemented
|
||||
|
||||
class ContractNotDeployed(Exception):
|
||||
pass
|
||||
|
||||
def __init__(self, agent, *args, **kwargs):
|
||||
contract = agent._blockchain._chain.provider.get_contract(agent._contract_name)
|
||||
self._contract = contract
|
||||
self._blockchain = agent._blockchain
|
||||
if not self._blockchain:
|
||||
self._blockchain = agent._blockchain
|
||||
|
||||
contract = self._blockchain._chain.provider.get_contract(self._principal_contract_name)
|
||||
self.__contract = contract
|
||||
|
||||
def __init_subclass__(cls, deployer):
|
||||
cls._deployer = deployer
|
||||
cls._principal_contract_name = deployer.contract_name()
|
||||
|
||||
def __repr__(self):
|
||||
class_name = self.__class__.__name__
|
||||
r = "{}(blockchain={}, contract={})"
|
||||
return r.format(class_name, self._blockchain, self._contract)
|
||||
return r.format(class_name, self._blockchain, self.__contract)
|
||||
|
||||
def call(self):
|
||||
return self._contract.call()
|
||||
return self.__contract.call()
|
||||
|
||||
def transact(self, *args, **kwargs):
|
||||
return self._contract.transact(*args, **kwargs)
|
||||
return self.__contract.transact(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def contract_address(self):
|
||||
return self.__contract.address
|
||||
|
||||
@property
|
||||
def contract_name(self):
|
||||
return self._principal_contract_name
|
||||
|
|
|
@ -12,13 +12,14 @@ class TheBlockchain:
|
|||
temp: Local private chain whos data directory is removed when the chain is shutdown. Runs via geth.
|
||||
"""
|
||||
|
||||
__network = ''
|
||||
_network = ''
|
||||
__instance = None
|
||||
_default_timeout = 60
|
||||
|
||||
class AlreadyRunning(Exception):
|
||||
class IsAlreadyRunning(Exception):
|
||||
pass
|
||||
|
||||
def __init__(self, populus_config: PopulusConfig=None, timeout=60):
|
||||
def __init__(self, populus_config: PopulusConfig=None, timeout=None):
|
||||
"""
|
||||
Configures a populus project and connects to blockchain.network.
|
||||
Transaction timeouts specified measured in seconds.
|
||||
|
@ -29,19 +30,21 @@ class TheBlockchain:
|
|||
|
||||
# Singleton
|
||||
if TheBlockchain.__instance is not None:
|
||||
message = 'Blockchain: is already running. Use .get() to retrieve'.format(self.__network)
|
||||
raise TheBlockchain.AlreadyRunning(message)
|
||||
message = 'Blockchain:{} is already running. Use .get() to retrieve'.format(self._network)
|
||||
raise TheBlockchain.IsAlreadyRunning(message)
|
||||
TheBlockchain.__instance = self
|
||||
|
||||
if populus_config is None:
|
||||
populus_config = PopulusConfig()
|
||||
|
||||
self._populus_config = populus_config
|
||||
self._timeout = timeout
|
||||
self._project = populus_config.project
|
||||
|
||||
# Opens and preserves connection to a running populus blockchain
|
||||
self._chain = self._project.get_chain(self.__network).__enter__()
|
||||
self._chain = self._project.get_chain(self._network).__enter__()
|
||||
|
||||
if timeout is None:
|
||||
timeout = TheBlockchain._default_timeout
|
||||
self._timeout = timeout
|
||||
|
||||
@classmethod
|
||||
def get(cls):
|
||||
|
@ -58,8 +61,8 @@ class TheBlockchain:
|
|||
|
||||
def __repr__(self):
|
||||
class_name = self.__class__.__name__
|
||||
r = "{}(network={}, timeout={})"
|
||||
return r.format(class_name, self.__network, self._timeout)
|
||||
r = "{}(network={})"
|
||||
return r.format(class_name, self._network)
|
||||
|
||||
def get_contract(self, name):
|
||||
"""
|
||||
|
|
|
@ -28,7 +28,7 @@ class MinerConfig:
|
|||
__max_allowed_locked = 10 ** 7 * TokenConfig._M
|
||||
|
||||
__reward = TokenConfig._reward
|
||||
__null_addr = '0x' + '0' * 40
|
||||
_null_addr = '0x' + '0' * 40
|
||||
|
||||
__mining_coeff = [
|
||||
__hours_per_period,
|
||||
|
@ -61,7 +61,7 @@ class MinerConfig:
|
|||
|
||||
@property
|
||||
def null_address(self):
|
||||
return self.__null_addr
|
||||
return self._null_addr
|
||||
|
||||
@property
|
||||
def mining_coefficient(self):
|
||||
|
|
|
@ -10,7 +10,7 @@ def testerchain():
|
|||
chain = TesterBlockchain()
|
||||
yield chain
|
||||
del chain
|
||||
TesterBlockchain.__instance = None
|
||||
TheBlockchain._TheBlockchain__instance = None
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
|
|
|
@ -23,8 +23,8 @@ def test_create_escrow(testerchain):
|
|||
same_token.arm()
|
||||
same_token.deploy()
|
||||
|
||||
assert len(token._contract.address) == 42
|
||||
assert token._contract.address == same_token._contract.address
|
||||
assert len(token.__contract.address) == 42
|
||||
assert token.__contract.address == same_token._contract.address
|
||||
|
||||
with raises(NoKnownAddress):
|
||||
MinerAgent.get(token=token)
|
||||
|
@ -38,8 +38,8 @@ def test_create_escrow(testerchain):
|
|||
same_escrow.arm()
|
||||
same_escrow.deploy()
|
||||
|
||||
assert len(escrow._contract.address) == 42
|
||||
assert escrow._contract.address == same_escrow._contract.address
|
||||
assert len(escrow.__contract.address) == 42
|
||||
assert escrow.__contract.address == same_escrow._contract.address
|
||||
|
||||
|
||||
def test_get_swarm(testerchain, token, escrow):
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
from os.path import join, dirname, abspath
|
||||
|
||||
import nkms_eth
|
||||
from nkms_eth.token import NuCypherKMSTokenAgent
|
||||
from nkms_eth.agents import NuCypherKMSTokenAgent
|
||||
from nkms_eth.deployers import NuCypherKMSTokenDeployer
|
||||
|
||||
|
||||
def test_testerchain_create(testerchain):
|
||||
def test_testerchain_creation(testerchain):
|
||||
# Ensure we are testing on the correct network...
|
||||
assert testerchain._network == 'tester'
|
||||
|
||||
|
@ -25,5 +26,5 @@ def test_nucypher_populus_project(testerchain):
|
|||
assert testerchain._project.project_dir == populus_project_dir
|
||||
|
||||
# Ensure that solidity smart contacts are available, post-compile.
|
||||
token_contract_identifier = NuCypherKMSTokenAgent._NuCypherKMSToken__contract_name
|
||||
token_contract_identifier = NuCypherKMSTokenDeployer(blockchain=testerchain).contract_name()
|
||||
assert token_contract_identifier in testerchain._project.compiled_contract_data
|
|
@ -1,13 +1,30 @@
|
|||
from nkms_eth.blockchain import TheBlockchain
|
||||
from nkms_eth.escrow import MinerAgent
|
||||
from nkms_eth.deployers import MinerEscrowDeployer, NuCypherKMSTokenDeployer
|
||||
|
||||
|
||||
class TesterBlockchain(TheBlockchain):
|
||||
"""Transient chain"""
|
||||
__network = 'tester'
|
||||
_network = 'tester'
|
||||
|
||||
|
||||
class MockMinerEscrow(MinerAgent):
|
||||
class MockMinerEscrowDeployer(MinerEscrowDeployer):
|
||||
"""Speed things up a bit"""
|
||||
__hours_per_period = 1
|
||||
__min_release_periods = 1
|
||||
|
||||
|
||||
class MockNuCypherKMSTokenDeployer(NuCypherKMSTokenDeployer):
|
||||
|
||||
def _global_airdrop(self, amount: int):
|
||||
"""Airdrops from creator address to all other addresses!"""
|
||||
|
||||
_creator, *addresses = self._blockchain._chain.web3.eth.accounts
|
||||
|
||||
def txs():
|
||||
for address in addresses:
|
||||
yield self._contract.transact({'from': self.origin}).transfer(address, amount * (10 ** 6))
|
||||
|
||||
for tx in txs():
|
||||
self._blockchain._chain.wait.for_receipt(tx, timeout=10)
|
||||
|
||||
return self
|
||||
|
|
Loading…
Reference in New Issue