示例#1
0
    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
示例#2
0
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()
示例#3
0
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
示例#4
0
    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()
示例#6
0
文件: actors.py 项目: mallek/nucypher
    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)
示例#7
0
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()
示例#8
0
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()
示例#9
0
    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")
示例#10
0
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()
示例#11
0
文件: actors.py 项目: mallek/nucypher
    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,
        }
示例#12
0
    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()
示例#13
0
文件: actors.py 项目: mallek/nucypher
    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)
示例#14
0
    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)
示例#15
0
 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(
     )
示例#16
0
    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
示例#17
0
    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
示例#18
0
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
示例#19
0
    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()
示例#20
0
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)
示例#21
0
    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
示例#22
0
    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()
示例#23
0
    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")
示例#24
0
    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
示例#25
0
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()
示例#26
0
 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)
示例#27
0
文件: actors.py 项目: mallek/nucypher
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