def __init__(self, checksum_address: str, policy_agent=None, economics: TokenEconomics = None, *args, **kwargs) -> None: """ :param policy_agent: A policy agent with the blockchain attached; If not passed, a default policy agent and blockchain connection will be created from default values. """ super().__init__(checksum_address=checksum_address, *args, **kwargs) if not policy_agent: # From defaults self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) self.miner_agent = MinerAgent(blockchain=self.blockchain) self.policy_agent = PolicyAgent(blockchain=self.blockchain) else: # Injected self.policy_agent = policy_agent if not economics: economics = TokenEconomics() self.economics = economics
def test_nucypher_deploy_contracts(testerchain, click_runner, mock_primary_registry_filepath): # We start with a blockchain node, and nothing else... assert not os.path.isfile(mock_primary_registry_filepath) command = ( 'contracts', '--registry-outfile', mock_primary_registry_filepath, '--provider-uri', TEST_PROVIDER_URI, '--poa', ) user_input = 'Y\n' + f'{INSECURE_DEVELOPMENT_PASSWORD}\n' * 6 result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # Check that the primary contract registry was written assert os.path.isfile(mock_primary_registry_filepath) # Now show that we can use contract Agency and read from the blockchain token_agent = NucypherTokenAgent() assert token_agent.get_balance() == 0 miner_agent = MinerAgent() assert miner_agent.get_current_period() testerchain.sever_connection()
def test_init_ursula_stake(click_runner, configuration_file_location, funded_blockchain, stake_value, token_economics): stake_args = ('ursula', 'stake', '--config-file', configuration_file_location, '--value', stake_value.to_tokens(), '--duration', token_economics.minimum_locked_periods, '--poa', '--force') result = click_runner.invoke(nucypher_cli, stake_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 with open(configuration_file_location, 'r') as config_file: config_data = json.loads(config_file.read()) # Verify the stake is on-chain miner_agent = MinerAgent() stakes = list( miner_agent.get_all_stakes( miner_address=config_data['checksum_public_address'])) assert len(stakes) == 1 start_period, end_period, value = stakes[0] assert NU(int(value), 'NuNit') == stake_value
def __init__(self, is_me: bool, start_staking_loop: bool = True, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.log = Logger("miner") self.is_me = is_me if is_me: self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) # Staking Loop self.__current_period = None self._abort_on_staking_error = True self._staking_task = task.LoopingCall(self._confirm_period) else: self.token_agent = constants.STRANGER_MINER self.miner_agent = MinerAgent(blockchain=self.blockchain) self.__stakes = constants.NO_STAKES self.__start_time = constants.NO_STAKES self.__uptime_period = constants.NO_STAKES self.__terminal_period = constants.NO_STAKES self.__read_stakes() if self.stakes and start_staking_loop: self.stake()
def test_nucypher_deploy_contracts(testerchain, click_runner, mock_primary_registry_filepath): # We start with a blockchain node, and nothing else... assert not os.path.isfile(mock_primary_registry_filepath) command = ('contracts', '--registry-outfile', mock_primary_registry_filepath, '--provider-uri', TEST_PROVIDER_URI, '--poa') user_input = 'Y\n' + f'{INSECURE_DEVELOPMENT_PASSWORD}\n' * 8 result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # Ensure there is a report on each contract for registry_name in Deployer.contract_names: assert registry_name in result.output # Check that the primary contract registry was written # and peek at some of the registered entries assert os.path.isfile(mock_primary_registry_filepath) with open(mock_primary_registry_filepath, 'r') as file: # Ensure every contract's name was written to the file, somehow raw_registry_data = file.read() for registry_name in Deployer.contract_names: assert registry_name in raw_registry_data # Ensure the Registry is JSON deserializable registry_data = json.loads(raw_registry_data) # and that is has the correct number of entries assert len(registry_data) == 9 # Read several records token_record, escrow_record, dispatcher_record, *other_records = registry_data registered_name, registered_address, registered_abi = token_record token_agent = NucypherTokenAgent() assert token_agent.contract_name == registered_name assert token_agent.registry_contract_name == registered_name assert token_agent.contract_address == registered_address # Now show that we can use contract Agency and read from the blockchain assert token_agent.get_balance() == 0 miner_agent = MinerAgent() assert miner_agent.get_current_period() # and at least the others can be instantiated assert PolicyAgent() assert MiningAdjudicatorAgent() testerchain.sever_connection()
def __init__(self, checksum_address: str, *args, **kwargs) -> None: """ :param policy_agent: A policy agent with the blockchain attached; If not passed, A default policy agent and blockchain connection will be created from default values. """ super().__init__(checksum_address=checksum_address, *args, **kwargs) # From defaults self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) self.miner_agent = MinerAgent(blockchain=self.blockchain) self.policy_agent = PolicyAgent(blockchain=self.blockchain)
def test_deploy_ethereum_contracts(chain): """ Launch all ethereum contracts: - NuCypherToken - PolicyManager - MinersEscrow - UserEscrow - Issuer """ token_deployer = NucypherTokenDeployer(blockchain=chain) token_deployer.arm() token_deployer.deploy() token_agent = NucypherTokenAgent(blockchain=chain) miner_escrow_deployer = MinerEscrowDeployer(token_agent=token_agent) miner_escrow_deployer.arm() miner_escrow_deployer.deploy() miner_agent = MinerAgent(token_agent=token_agent) policy_manager_contract = PolicyManagerDeployer(miner_agent=miner_agent) policy_manager_contract.arm() policy_manager_contract.deploy()
def test_deploy_ethereum_contracts(testerchain): """ A bare minimum nucypher deployment fixture. """ origin, *everybody_else = testerchain.interface.w3.eth.accounts token_deployer = NucypherTokenDeployer(blockchain=testerchain, deployer_address=origin) token_deployer.arm() token_deployer.deploy() token_agent = NucypherTokenAgent(blockchain=testerchain) miners_escrow_secret = os.urandom(constants.DISPATCHER_SECRET_LENGTH) miner_escrow_deployer = MinerEscrowDeployer( token_agent=token_agent, deployer_address=origin, secret_hash=testerchain.interface.w3.sha3(miners_escrow_secret)) miner_escrow_deployer.arm() miner_escrow_deployer.deploy() miner_agent = MinerAgent(token_agent=token_agent) policy_manager_secret = os.urandom(constants.DISPATCHER_SECRET_LENGTH) policy_manager_deployer = PolicyManagerDeployer( miner_agent=miner_agent, deployer_address=origin, secret_hash=testerchain.interface.w3.sha3(policy_manager_secret)) policy_manager_deployer.arm() policy_manager_deployer.deploy() policy_agent = policy_manager_deployer.make_agent()
def __init__(self, blockchain: Blockchain, deployer_address: str = None, bare: bool = True) -> None: self.blockchain = blockchain self.__deployer_address = NO_DEPLOYER_ADDRESS if deployer_address: self.deployer_address = deployer_address if not bare: self.token_agent = NucypherTokenAgent(blockchain=blockchain) self.miner_agent = MinerAgent(blockchain=blockchain) self.policy_agent = PolicyAgent(blockchain=blockchain) self.adjudicator_agent = MiningAdjudicatorAgent( blockchain=blockchain) self.user_escrow_deployers = dict() self.deployers = { NucypherTokenDeployer.contract_name: self.deploy_token_contract, MinerEscrowDeployer.contract_name: self.deploy_miner_contract, PolicyManagerDeployer.contract_name: self.deploy_policy_contract, UserEscrowProxyDeployer.contract_name: self.deploy_escrow_proxy, MiningAdjudicatorDeployer.contract_name: self.deploy_mining_adjudicator_contract, } self.log = Logger("Deployment-Actor")
def test_deploy_ethereum_contracts(testerchain): """ Launch all ethereum contracts: - NuCypherToken - PolicyManager - MinersEscrow - UserEscrow - Issuer """ origin, *everybody_else = testerchain.interface.w3.eth.accounts token_deployer = NucypherTokenDeployer(blockchain=testerchain, deployer_address=origin) token_deployer.arm() token_deployer.deploy() token_agent = NucypherTokenAgent(blockchain=testerchain) miner_escrow_deployer = MinerEscrowDeployer(token_agent=token_agent, deployer_address=origin) miner_escrow_deployer.arm() miner_escrow_deployer.deploy() miner_agent = MinerAgent(token_agent=token_agent) policy_manager_deployer = PolicyManagerDeployer(miner_agent=miner_agent, deployer_address=origin) policy_manager_deployer.arm() policy_manager_deployer.deploy() policy_agent = policy_manager_deployer.make_agent()
def __init__(self, blockchain: Blockchain, deployer_address: str = None, allocation_registry: AllocationRegistry = None, bare: bool = True ) -> None: self.blockchain = blockchain self.__deployer_address = NO_DEPLOYER_ADDRESS if deployer_address: self.deployer_address = deployer_address if not bare: self.token_agent = NucypherTokenAgent(blockchain=blockchain) self.miner_agent = MinerAgent(blockchain=blockchain) self.policy_agent = PolicyAgent(blockchain=blockchain) self.allocation_registy = allocation_registry self.user_escrow_deployers = dict() self.deployers = { NucypherTokenDeployer.contract_name: self.deploy_token_contract, MinerEscrowDeployer.contract_name: self.deploy_miner_contract, PolicyManagerDeployer.contract_name: self.deploy_policy_contract, UserEscrowProxyDeployer.contract_name: self.deploy_escrow_proxy, }
def __init__(self, is_me: bool, start_staking_loop: bool = True, economics: TokenEconomics = None, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.log = Logger("miner") self.is_me = is_me if not economics: economics = TokenEconomics() self.economics = economics # # Blockchain # if is_me: self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) # Staking Loop self.__current_period = None self._abort_on_staking_error = True self._staking_task = task.LoopingCall(self.heartbeat) else: self.token_agent = STRANGER_MINER self.miner_agent = MinerAgent(blockchain=self.blockchain) # # Stakes # self.__stakes = UNKNOWN_STAKES self.__start_time = NOT_STAKING self.__uptime_period = NOT_STAKING self.__terminal_period = UNKNOWN_STAKES self.__read_stakes() # "load-in": Read on-chain stakes # Start the callbacks if there are active stakes if (self.stakes is not NO_STAKES) and start_staking_loop: self.stake()
def __init__(self, is_me: bool, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.log = Logger("miner") self.is_me = is_me if is_me: self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) # Staking Loop self.__current_period = None self._abort_on_staking_error = True self._staking_task = task.LoopingCall(self._confirm_period) else: self.token_agent = constants.STRANGER_MINER # Everyone! self.miner_agent = MinerAgent(blockchain=self.blockchain)
def connect_to_contracts(self, simulation: bool = False): """Initialize contract agency and set them on config""" if simulation is True: self.blockchain.interface._registry._swap_registry( filepath=self.sim_registry_filepath) self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) self.miner_agent = MinerAgent(token_agent=self.token_agent) self.policy_agent = PolicyAgent(miner_agent=self.miner_agent)
def __init__(self, allocation_registry: AllocationRegistry = None, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) self.miner_agent = MinerAgent(blockchain=self.blockchain) self.policy_agent = PolicyAgent(blockchain=self.blockchain) self.__beneficiary_address = NO_BENEFICIARY self.__allocation_registry = allocation_registry or self.__allocation_registry( )
def __init__(self, is_me=True, miner_agent: MinerAgent = None, *args, **kwargs): miner_agent = miner_agent if miner_agent is not None else MinerAgent() super().__init__(token_agent=miner_agent.token_agent, *args, **kwargs) # Extrapolate dependencies self.miner_agent = miner_agent self.token_agent = miner_agent.token_agent self.blockchain = self.token_agent.blockchain # Establish initial state self.is_me = is_me
def __init__(self, policy_agent: PolicyAgent=None, *args, **kwargs): if policy_agent is None: # all defaults token_agent = NucypherTokenAgent() miner_agent = MinerAgent(token_agent=token_agent) policy_agent = PolicyAgent(miner_agent=miner_agent) self.policy_agent = policy_agent super().__init__(token_agent=self.policy_agent.token_agent, *args, **kwargs) self._arrangements = OrderedDict() # Track authored policies by id
class PolicyAuthor(NucypherTokenActor): """Alice base class for blockchain operations, mocking up new policies!""" def __init__(self, checksum_address: str, policy_agent=None, economics: TokenEconomics = None, *args, **kwargs) -> None: """ :param policy_agent: A policy agent with the blockchain attached; If not passed, a default policy agent and blockchain connection will be created from default values. """ super().__init__(checksum_address=checksum_address, *args, **kwargs) if not policy_agent: # From defaults self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) self.miner_agent = MinerAgent(blockchain=self.blockchain) self.policy_agent = PolicyAgent(blockchain=self.blockchain) else: # Injected self.policy_agent = policy_agent if not economics: economics = TokenEconomics() self.economics = economics def recruit(self, quantity: int, **options) -> List[str]: """ Uses sampling logic to gather miners from the blockchain and caches the resulting node ethereum addresses. :param quantity: Number of ursulas to sample from the blockchain. """ miner_addresses = self.miner_agent.sample(quantity=quantity, **options) return miner_addresses def create_policy(self, *args, **kwargs): """ Hence the name, a PolicyAuthor can create a BlockchainPolicy with themself as the author. :return: Returns a newly authored BlockchainPolicy with n proposed arrangements. """ from nucypher.blockchain.eth.policies import BlockchainPolicy blockchain_policy = BlockchainPolicy(alice=self, *args, **kwargs) return blockchain_policy
def __init__(self, miner_agent: MinerAgent=None, *args, **kwargs): if miner_agent is None: miner_agent = MinerAgent(token_agent=NucypherTokenAgent()) super().__init__(token_agent=miner_agent.token_agent, *args, **kwargs) self.miner_agent = miner_agent miner_agent.miners.append(self) # Track Miners self.token_agent = miner_agent.token_agent self.blockchain = self.token_agent.blockchain self.__locked_tokens = None self.__update_locked_tokens()
def test_init_ursula_stake(click_runner, configuration_file_location, funded_blockchain): stake_args = ('ursula', 'stake', '--config-file', configuration_file_location, '--value', STAKE_VALUE, '--duration', MIN_LOCKED_PERIODS, '--poa', '--force') result = click_runner.invoke(nucypher_cli, stake_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 with open(configuration_file_location, 'r') as config_file: config_data = json.loads(config_file.read()) # Verify the stake is on-chain miner_agent = MinerAgent() stakes = list( miner_agent.get_all_stakes( miner_address=config_data['checksum_public_address'])) assert len(stakes) == 1 start_period, end_period, value = stakes[0] assert Web3.fromWei(value, 'ether') == int(STAKE_VALUE)
def __init__(self, miner_agent: MinerAgent, is_me=True, *args, **kwargs) -> None: if miner_agent is None: token_agent = NucypherTokenAgent() miner_agent = MinerAgent(token_agent=token_agent) super().__init__(token_agent=miner_agent.token_agent, *args, **kwargs) # Extrapolate dependencies self.miner_agent = miner_agent self.token_agent = miner_agent.token_agent self.blockchain = self.token_agent.blockchain # Establish initial state self.is_me = is_me
def __init__(self, blockchain: Blockchain, deployer_address: str = None, allocation_registry: AllocationRegistry = None, bare: bool = True) -> None: self.blockchain = blockchain self.__deployer_address = NO_DEPLOYER_ADDRESS if deployer_address: self.deployer_address = deployer_address if not bare: self.token_agent = NucypherTokenAgent(blockchain=blockchain) self.miner_agent = MinerAgent(blockchain=blockchain) self.policy_agent = PolicyAgent(blockchain=blockchain) self.allocation_registy = allocation_registry self.user_escrow_deployers = dict()
def __init__(self, blockchain: Blockchain, deployer_address: str = None, bare: bool = True) -> None: self.blockchain = blockchain self.__deployer_address = NO_DEPLOYER_ADDRESS self.deployer_address = deployer_address self.checksum_address = self.deployer_address if not bare: self.token_agent = NucypherTokenAgent(blockchain=blockchain) self.miner_agent = MinerAgent(blockchain=blockchain) self.policy_agent = PolicyAgent(blockchain=blockchain) self.adjudicator_agent = MiningAdjudicatorAgent( blockchain=blockchain) self.user_escrow_deployers = dict() self.deployers = {d.contract_name: d for d in self.deployers} self.log = Logger("Deployment-Actor")
def produce(self, passphrase: str = None, **overrides): """Produce a new Ursula from configuration""" if not self.temp: self.read_keyring() self.keyring.unlock(passphrase=passphrase) merged_parameters = { **self.static_payload, **self.dynamic_payload, **overrides } if self.federated_only is False: if self.poa: # TODO: move this..? w3 = self.miner_agent.blockchain.interface.w3 w3.middleware_stack.inject(geth_poa_middleware, layer=0) if not self.miner_agent: # TODO: move this..? self.blockchain = Blockchain.connect( provider_uri=self.blockchain_uri, registry_filepath=self.registry_filepath) self.token_agent = NucypherTokenAgent( blockchain=self.blockchain) self.miner_agent = MinerAgent(token_agent=self.token_agent) merged_parameters.update(miner_agent=self.miner_agent) ursula = self._Character(**merged_parameters) if self.temp: # TODO: Move this..? class MockDatastoreThreadPool(object): def callInThread(self, f, *args, **kwargs): return f(*args, **kwargs) ursula.datastore_threadpool = MockDatastoreThreadPool() return ursula
def test_token_deployer_and_agent(testerchain): origin, *everybody_else = testerchain.interface.w3.eth.accounts # The big day... token_deployer = NucypherTokenDeployer(blockchain=testerchain, deployer_address=origin) token_deployer.deploy() secret_hash = os.urandom(32) deployer = MinerEscrowDeployer(blockchain=testerchain, deployer_address=origin, secret_hash=secret_hash) deployment_txhashes = deployer.deploy() for title, txhash in deployment_txhashes.items(): receipt = testerchain.wait_for_receipt(txhash=txhash) assert receipt['status'] == 1, "Transaction Rejected {}:{}".format( title, txhash) # Create a token instance miner_agent = deployer.make_agent() miner_escrow_contract = miner_agent.contract expected_token_supply = miner_escrow_contract.functions.totalSupply().call( ) assert expected_token_supply == miner_agent.contract.functions.totalSupply( ).call() # Retrieve the token from the blockchain same_miner_agent = MinerAgent() # Compare the contract address for equality assert miner_agent.contract_address == same_miner_agent.contract_address assert miner_agent == same_miner_agent # __eq__ testerchain.interface.registry.clear()
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) self.miner_agent = MinerAgent(blockchain=self.blockchain) self.policy_agent = PolicyAgent(blockchain=self.blockchain)
class Miner(NucypherTokenActor): """ Ursula baseclass for blockchain operations, practically carrying a pickaxe. """ __current_period_sample_rate = 10 class MinerError(NucypherTokenActor.ActorError): pass def __init__(self, is_me: bool, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.log = Logger("miner") self.is_me = is_me if is_me: self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) # Staking Loop self.__current_period = None self._abort_on_staking_error = True self._staking_task = task.LoopingCall(self._confirm_period) else: self.token_agent = constants.STRANGER_MINER # Everyone! self.miner_agent = MinerAgent(blockchain=self.blockchain) # # Staking # @only_me def stake(self, confirm_now=False, resume: bool = False, expiration: maya.MayaDT = None, lock_periods: int = None, *args, **kwargs) -> None: """High-level staking daemon loop""" if lock_periods and expiration: raise ValueError("Pass the number of lock periods or an expiration MayaDT; not both.") if expiration: lock_periods = datetime_to_period(expiration) if resume is False: _staking_receipts = self.initialize_stake(expiration=expiration, lock_periods=lock_periods, *args, **kwargs) # TODO: Check if this period has already been confirmed # TODO: Check if there is an active stake in the current period: Resume staking daemon # TODO: Validation and Sanity checks if confirm_now: self.confirm_activity() # record start time and periods self.__start_time = maya.now() self.__uptime_period = self.miner_agent.get_current_period() self.__terminal_period = self.__uptime_period + lock_periods self.__current_period = self.__uptime_period self.start_staking_loop() # # Daemon # @only_me def _confirm_period(self): period = self.miner_agent.get_current_period() self.log.info("Checking for new period. Current period is {}".format(self.__current_period)) # TODO: set to debug? if self.__current_period != period: # check for stake expiration stake_expired = self.__current_period >= self.__terminal_period if stake_expired: self.log.info('Stake duration expired') return True self.confirm_activity() self.__current_period = period self.log.info("Confirmed activity for period {}".format(self.__current_period)) @only_me def _crash_gracefully(self, failure=None): """ A facility for crashing more gracefully in the event that an exception is unhandled in a different thread, especially inside a loop like the learning loop. """ self._crashed = failure failure.raiseException() @only_me def handle_staking_errors(self, *args, **kwargs): failure = args[0] if self._abort_on_staking_error: self.log.critical("Unhandled error during node staking. Attempting graceful crash.") reactor.callFromThread(self._crash_gracefully, failure=failure) else: self.log.warn("Unhandled error during node learning: {}".format(failure.getTraceback())) @only_me def start_staking_loop(self, now=True): if self._staking_task.running: return False else: d = self._staking_task.start(interval=self.__current_period_sample_rate, now=now) d.addErrback(self.handle_staking_errors) self.log.info("Started staking loop") return d @property def is_staking(self): """Checks if this Miner currently has locked tokens.""" return bool(self.locked_tokens > 0) @property def locked_tokens(self): """Returns the amount of tokens this miner has locked.""" return self.miner_agent.get_locked_tokens(miner_address=self.checksum_public_address) @property def stakes(self) -> Tuple[list]: """Read all live stake data from the blockchain and return it as a tuple""" stakes_reader = self.miner_agent.get_all_stakes(miner_address=self.checksum_public_address) return tuple(stakes_reader) @only_me def deposit(self, amount: int, lock_periods: int) -> Tuple[str, str]: """Public facing method for token locking.""" approve_txhash = self.token_agent.approve_transfer(amount=amount, target_address=self.miner_agent.contract_address, sender_address=self.checksum_public_address) deposit_txhash = self.miner_agent.deposit_tokens(amount=amount, lock_periods=lock_periods, sender_address=self.checksum_public_address) return approve_txhash, deposit_txhash @only_me def divide_stake(self, stake_index: int, target_value: int, additional_periods: int = None, expiration: maya.MayaDT = None) -> dict: """ Modifies the unlocking schedule and value of already locked tokens. This actor requires that is_me is True, and that the expiration datetime is after the existing locking schedule of this miner, or an exception will be raised. :param target_value: The quantity of tokens in the smallest denomination. :param expiration: The new expiration date to set. :return: Returns the blockchain transaction hash """ if additional_periods and expiration: raise ValueError("Pass the number of lock periods or an expiration MayaDT; not both.") _first_period, last_period, locked_value = self.miner_agent.get_stake_info( miner_address=self.checksum_public_address, stake_index=stake_index) if expiration: additional_periods = datetime_to_period(datetime=expiration) - last_period if additional_periods <= 0: raise self.MinerError("Expiration {} must be at least 1 period from now.".format(expiration)) if target_value >= locked_value: raise self.MinerError("Cannot divide stake; Value must be less than the specified stake value.") # Ensure both halves are for valid amounts validate_stake_amount(amount=target_value) validate_stake_amount(amount=locked_value - target_value) tx = self.miner_agent.divide_stake(miner_address=self.checksum_public_address, stake_index=stake_index, target_value=target_value, periods=additional_periods) self.blockchain.wait_for_receipt(tx) return tx @only_me def __validate_stake(self, amount: int, lock_periods: int) -> bool: assert validate_stake_amount(amount=amount) # TODO: remove assertions..? assert validate_locktime(lock_periods=lock_periods) if not self.token_balance >= amount: raise self.MinerError("Insufficient miner token balance ({balance})".format(balance=self.token_balance)) else: return True @only_me def initialize_stake(self, amount: int, lock_periods: int = None, expiration: maya.MayaDT = None, entire_balance: bool = False) -> dict: """ High level staking method for Miners. :param amount: Amount of tokens to stake denominated in the smallest unit. :param lock_periods: Duration of stake in periods. :param expiration: A MayaDT object representing the time the stake expires; used to calculate lock_periods. :param entire_balance: If True, stake the entire balance of this node, or the maximum possible. """ if lock_periods and expiration: raise ValueError("Pass the number of lock periods or an expiration MayaDT; not both.") if entire_balance and amount: raise self.MinerError("Specify an amount or entire balance, not both") if expiration: lock_periods = calculate_period_duration(future_time=expiration) if entire_balance is True: amount = self.token_balance staking_transactions = OrderedDict() # type: OrderedDict # Time series of txhases # Validate assert self.__validate_stake(amount=amount, lock_periods=lock_periods) # Transact approve_txhash, initial_deposit_txhash = self.deposit(amount=amount, lock_periods=lock_periods) self._transaction_cache.append((datetime.utcnow(), initial_deposit_txhash)) self.log.info("{} Initialized new stake: {} tokens for {} periods".format(self.checksum_public_address, amount, lock_periods)) return staking_transactions # # Reward and Collection # @only_me def confirm_activity(self) -> str: """Miner rewarded for every confirmed period""" txhash = self.miner_agent.confirm_activity(node_address=self.checksum_public_address) self._transaction_cache.append((datetime.utcnow(), txhash)) return txhash @only_me def mint(self) -> Tuple[str, str]: """Computes and transfers tokens to the miner's account""" mint_txhash = self.miner_agent.mint(node_address=self.checksum_public_address) self._transaction_cache.append((datetime.utcnow(), mint_txhash)) return mint_txhash @only_me def collect_policy_reward(self, policy_manager): """Collect rewarded ETH""" policy_reward_txhash = policy_manager.collect_policy_reward(collector_address=self.checksum_public_address) self._transaction_cache.append((datetime.utcnow(), policy_reward_txhash)) return policy_reward_txhash @only_me def collect_staking_reward(self, collector_address: str) -> str: """Withdraw tokens rewarded for staking.""" collection_txhash = self.miner_agent.collect_staking_reward(collector_address=collector_address) self._transaction_cache.append((datetime.utcnow(), collection_txhash)) return collection_txhash